From a64af2e2d74ff562ecb8b557655fa42b97f6e412 Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Wed, 11 May 2022 15:04:42 +0800 Subject: [PATCH 01/40] Minor doc tf 2.8 change (#1934) Signed-off-by: Deyu Huang Co-authored-by: Jay Zhang --- README.md | 4 ++-- Troubleshooting.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fb5d52ed3..8d536c636 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ The common issues we run into we try to document here [Troubleshooting Guide](Tr | Build Type | OS | Python | Tensorflow | ONNX opset | Status | | --- | --- | --- | --- | --- | --- | -| Unit Test - Basic | Linux, MacOS\*, Windows\* | 3.6-3.9 | 1.12-1.15, 2.1-2.7 | 9-15 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test?branchName=master)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=16&branchName=master) | -| Unit Test - Full | Linux, MacOS, Windows | 3.6-3.9 | 1.12-1.15, 2.1-2.7 | 9-15 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test-matrix?branchName=master)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=18&branchName=master) | | +| Unit Test - Basic | Linux, MacOS\*, Windows\* | 3.6-3.9 | 1.12-1.15, 2.1-2.8 | 9-15 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=16&branchName=main) | +| Unit Test - Full | Linux, MacOS, Windows | 3.6-3.9 | 1.12-1.15, 2.1-2.8 | 9-15 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test-matrix?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=18&branchName=main) | |
## Supported Versions diff --git a/Troubleshooting.md b/Troubleshooting.md index 7eea0f50c..fd9ef4a6a 100644 --- a/Troubleshooting.md +++ b/Troubleshooting.md @@ -18,11 +18,11 @@ To get this fixed you can open an issue or send us a PR with a fix. Sometimes there is no direct mapping from tensorflow to ONNX. We took care are of the most common cases. But for less frequently used ops there might be a mapping missing. To get this fixed there 2 options: -a) in tf2onnx you can compose the op out of different ops. A good example for this the [Erf op](https://github.com/onnx/tensorflow-onnx/blob/master/tf2onnx/onnx_opset/math.py#L317). Before opset-9 this tf2onnx composes Erf with other ONNX ops. +a) in tf2onnx you can compose the op out of different ops. A good example for this the [Erf op](https://github.com/onnx/tensorflow-onnx/blob/main/tf2onnx/onnx_opset/math.py#L317). Before opset-9 this tf2onnx composes Erf with other ONNX ops. b) You request the missing op to be added to [ONNX](https://github.com/onnx/onnx). After it is added to ONNX and some runtime implements it we'll add it to tf2onnx. You can see that this happened for the Erf Op. Starting with opset-9, ONNX added it - tf2onnx no longer composes the op and instead passes it to ONNX. -c) The op is too complex to compose and it's to exotic to add to ONNX. In that cases you can use a custom op to implement it. Custom ops are documented in the [README](README.md) and there is an example [here](https://github.com/onnx/tensorflow-onnx/blob/master/examples/custom_op_via_python.py). There are 2 flavors of it: +c) The op is too complex to compose and it's to exotic to add to ONNX. In that cases you can use a custom op to implement it. Custom ops are documented in the [README](README.md) and there is an example [here](https://github.com/onnx/tensorflow-onnx/blob/main/examples/custom_op_via_python.py). There are 2 flavors of it: - you could compose the functionality by using multiple ONNX ops. - you can implement the op in your runtime as custom op (assuming that most runtimes do have such a mechanism) and then map it in tf2onnx as custom op. @@ -31,7 +31,7 @@ c) The op is too complex to compose and it's to exotic to add to ONNX. In that c There is a common group of errors that reports ```get tensor value: ... must be Const```. The reason for this is that there is a dynamic input of a tensorflow op but the equivalent ONNX op uses a static attribute. In other words in tensorflow that input is only known at runtime but in ONNX it need to be known at graph creation time. -An example of this is the [ONNX Slice operator before opset-10](https://github.com/onnx/onnx/blob/master/docs/Changelog.md#Slice-1) - the start and end of the slice are static attributes that need to be known at graph creation. In tensorflow the [strided slice op](https://www.tensorflow.org/api_docs/cc/class/tensorflow/ops/strided-slice) allows dynamic inputs. tf2onnx will try to find the real value of begin and end of the slice and can find them in most cases. But if those are real dynamic values calculate at runtime it will result in the message ```get tensor value: ... must be Const```. +An example of this is the [ONNX Slice operator before opset-10](https://github.com/onnx/onnx/blob/main/docs/Changelog.md#Slice-1) - the start and end of the slice are static attributes that need to be known at graph creation. In tensorflow the [strided slice op](https://www.tensorflow.org/api_docs/cc/class/tensorflow/ops/strided-slice) allows dynamic inputs. tf2onnx will try to find the real value of begin and end of the slice and can find them in most cases. But if those are real dynamic values calculate at runtime it will result in the message ```get tensor value: ... must be Const```. You can pass the options ```--fold_const```(removed after tf2onnx-1.9.3) in the tf2onnx command line that allows tf2onnx to apply more aggressive constant folding which will increase chances to find a constant. From 772dbe605e94880d4cbf8e9e1ed9ce9517b89c64 Mon Sep 17 00:00:00 2001 From: Chun-Wei Chen Date: Mon, 16 May 2022 18:10:53 -0700 Subject: [PATCH 02/40] Use make_tensor_sequence_value_info instead of deprecated make_sequence_value_info (#1930) Signed-off-by: Chun-Wei Chen --- tf2onnx/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tf2onnx/utils.py b/tf2onnx/utils.py index 4d5835cd4..9f3ba095d 100644 --- a/tf2onnx/utils.py +++ b/tf2onnx/utils.py @@ -169,7 +169,7 @@ def make_onnx_inputs_outputs(name, elem_type, shape, **kwargs): if elem_type is None: elem_type = onnx_pb.TensorProto.UNDEFINED elif isinstance(elem_type, SeqType): - return helper.make_sequence_value_info(name, elem_type.dtype, make_onnx_shape(shape), **kwargs) + return helper.make_tensor_sequence_value_info(name, elem_type.dtype, make_onnx_shape(shape), **kwargs) return helper.make_tensor_value_info( name, elem_type, From d4d9f068df759a20af418a7f616aa554269afc38 Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Wed, 18 May 2022 17:27:15 +0800 Subject: [PATCH 03/40] skip tfjs 3.17 tests (#1942) Signed-off-by: Deyu Huang --- tests/common.py | 25 +++++++++++++++++++++++++ tests/test_cond.py | 1 + tests/test_loops.py | 7 ++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/common.py b/tests/common.py index 80144bb91..f92fa1f51 100644 --- a/tests/common.py +++ b/tests/common.py @@ -24,6 +24,8 @@ "check_onnxruntime_backend", "check_tf_min_version", "check_tf_max_version", + "check_tfjs_min_version", + "check_tfjs_max_version", "skip_tf_versions", "skip_tf_cpu", "check_onnxruntime_min_version", @@ -272,6 +274,29 @@ def requires_custom_ops(message=""): can_import = False return unittest.skipIf(not can_import, reason) +def check_tfjs_max_version(max_accepted_version, message=""): + """ Skip if tfjs_version > max_required_version """ + config = get_test_config() + reason = _append_message("conversion requires tensorflowjs <= {}".format(max_accepted_version), message) + try: + import tensorflowjs + can_import = True + except ModuleNotFoundError: + can_import = False + return unittest.skipIf(can_import and not config.skip_tfjs_tests and \ + tensorflowjs.__version__ > LooseVersion(max_accepted_version), reason) + +def check_tfjs_min_version(min_required_version, message=""): + """ Skip if tjs_version < min_required_version """ + config = get_test_config() + reason = _append_message("conversion requires tensorflowjs >= {}".format(min_required_version), message) + try: + import tensorflowjs + can_import = True + except ModuleNotFoundError: + can_import = False + return unittest.skipIf(can_import and not config.skip_tfjs_tests and \ + tensorflowjs.__version__ < LooseVersion(min_required_version), reason) def check_tf_max_version(max_accepted_version, message=""): """ Skip if tf_version > max_required_version """ diff --git a/tests/test_cond.py b/tests/test_cond.py index 8d74c8dcc..7fa8c5dbc 100644 --- a/tests/test_cond.py +++ b/tests/test_cond.py @@ -118,6 +118,7 @@ def false_fn(): output_names_with_port = ["output:0"] self.run_test_case(func, feed_dict, input_names_with_port, output_names_with_port) + @check_tfjs_max_version("3.15", "failed when tfjs version > 3.15") def test_cond_in_while_loop(self): def func(i, inputs): inputs_2 = tf.identity(inputs) diff --git a/tests/test_loops.py b/tests/test_loops.py index b0b8f9213..410bee378 100644 --- a/tests/test_loops.py +++ b/tests/test_loops.py @@ -7,7 +7,8 @@ import tensorflow as tf from backend_test_base import Tf2OnnxBackendTestBase -from common import unittest_main, check_tf_min_version, check_tf_max_version, check_onnxruntime_min_version +from common import unittest_main, check_tf_min_version, check_tf_max_version, \ + check_onnxruntime_min_version, check_tfjs_max_version from tf2onnx.tf_loader import is_tf2 @@ -66,6 +67,7 @@ def func(i): x_val = np.array(3, dtype=np.int32) self.run_test_case(func, {_INPUT: x_val}, [], [_OUTPUT], rtol=1e-06) + @check_tfjs_max_version("3.15", "failed when tfjs version > 3.15") def test_while_loop_with_ta_write(self): def func(i): output_ta = tf.TensorArray(dtype=tf.int32, size=0, dynamic_size=True) @@ -159,6 +161,7 @@ def b(i, res, res2): output_names_with_port = ["i:0", "x:0", "y:0"] self.run_test_case(func, feed_dict, input_names_with_port, output_names_with_port, rtol=1e-06) + @check_tfjs_max_version("3.15", "failed when tfjs version > 3.15") def test_while_loop_with_ta_read_and_write(self): def func(i, inputs): inputs_2 = tf.identity(inputs) @@ -183,6 +186,7 @@ def b(i, out_ta): output_names_with_port = ["i:0", "output_ta:0"] self.run_test_case(func, feed_dict, input_names_with_port, output_names_with_port, rtol=1e-06) + @check_tfjs_max_version("3.15", "failed when tfjs version > 3.15") def test_while_loop_with_multi_scan_outputs(self): def func(i, inputs1, inputs2): inputs1_ = tf.identity(inputs1) @@ -217,6 +221,7 @@ def b(i, out_ta, out_ta2): output_names_with_port = ["i:0", "output_ta:0", "output_ta2:0"] self.run_test_case(func, feed_dict, input_names_with_port, output_names_with_port, rtol=1e-06) + @check_tfjs_max_version("3.15", "failed when tfjs version > 3.15") @check_onnxruntime_min_version( "0.5.0", "disable this case due to onnxruntime loop issue: https://github.com/microsoft/onnxruntime/issues/1272" From aa833046634fb6f15359c2526d021aed4a7aa0b8 Mon Sep 17 00:00:00 2001 From: Francesco Di Natale Date: Wed, 18 May 2022 08:41:15 -0700 Subject: [PATCH 04/40] Bugfix for concatenating node instead of str. (#1933) Signed-off-by: Francesco Di Natale Co-authored-by: Jay Zhang <36183870+fatcat-z@users.noreply.github.com> --- tf2onnx/graph.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tf2onnx/graph.py b/tf2onnx/graph.py index 18a16269b..32b8c69e3 100644 --- a/tf2onnx/graph.py +++ b/tf2onnx/graph.py @@ -751,10 +751,10 @@ def reset_nodes(self, ops): for n in self.inputs: if n not in ops: - raise ValueError("graph input " + n + " not exist") + raise ValueError("graph input '" + n.name + "' not exist") for o in self.outputs: if o not in self._output_to_node_name: - raise ValueError("graph output " + o + " not exist") + raise ValueError("graph output '" + o.name + "' not exist") self._dtypes = remained_dtypes self._output_shapes = remained_shapes From cc16eb9f8aeb0d10cb7a37509a0ed1990a33f6f3 Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Thu, 19 May 2022 11:10:24 +0800 Subject: [PATCH 05/40] Remove python 3.6 support, upgrade CI (#1940) * drop python36 unit tests Signed-off-by: Deyu Huang * add doc change Signed-off-by: Deyu Huang Co-authored-by: Jay Zhang --- README.md | 12 ++++++------ .../keras2onnx_application_tests.yml | 8 ++++---- .../azure_pipelines/keras2onnx_unit_test.yml | 16 ++++++++-------- .../onnxruntime_nightly_test.yml | 16 ++-------------- .../pretrained_model_test-matrix.yml | 13 ++----------- .../templates/combine_test_coverage.yml | 2 +- .../trimmed_keras2onnx_application_tests.yml | 4 ++-- .../trimmed_keras2onnx_unit_test.yml | 10 ++++++---- ci_build/azure_pipelines/unit_test-matrix.yml | 8 ++++---- ci_build/azure_pipelines/unit_test.yml | 17 ++++++++++++++--- setup.py | 1 - 11 files changed, 49 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 8d536c636..08fbef5b3 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,16 @@ __Note: tensorflow.js support was just added. While we tested it with many tfjs TensorFlow has many more ops than ONNX and occasionally mapping a model to ONNX creates issues. -You find a list of supported Tensorflow ops and their mapping to ONNX [here](support_status.md). +You find a list of supported TensorFlow ops and their mapping to ONNX [here](support_status.md). The common issues we run into we try to document here [Troubleshooting Guide](Troubleshooting.md).
-| Build Type | OS | Python | Tensorflow | ONNX opset | Status | +| Build Type | OS | Python | TensorFlow | ONNX opset | Status | | --- | --- | --- | --- | --- | --- | -| Unit Test - Basic | Linux, MacOS\*, Windows\* | 3.6-3.9 | 1.12-1.15, 2.1-2.8 | 9-15 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=16&branchName=main) | -| Unit Test - Full | Linux, MacOS, Windows | 3.6-3.9 | 1.12-1.15, 2.1-2.8 | 9-15 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test-matrix?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=18&branchName=main) | | +| Unit Test - Basic | Linux, MacOS\*, Windows\* | 3.7-3.9 | 1.13-1.15, 2.1-2.8 | 9-15 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=16&branchName=main) | +| Unit Test - Full | Linux, MacOS, Windows | 3.7-3.9 | 1.13-1.15, 2.1-2.8 | 9-15 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test-matrix?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=18&branchName=main) | |
## Supported Versions @@ -34,7 +34,7 @@ If you want the graph to be generated with a specific opset, use ```--opset``` i ### TensorFlow -We support ```tf-1.x graphs``` and ```tf-2.x```. To keep our test matrix manageable we test tf2onnx running on top of ```tf-1.12 or better```. +We support ```tf-1.x graphs``` and ```tf-2.x```. To keep our test matrix manageable we test tf2onnx running on top of ```tf-1.13 or better```. When running under tf-2.x tf2onnx will use the tensorflow V2 controlflow. @@ -42,7 +42,7 @@ You can install tf2onnx on top of tf-1.x or tf-2.x. ### Python -We support Python ```3.6-3.9```. +We support Python ```3.7-3.9```. Note that on windows for Python > 3.7 the protobuf package doesn't use the cpp implementation and is very slow - we recommend to use Python 3.7 for that reason. ## Prerequisites diff --git a/ci_build/azure_pipelines/keras2onnx_application_tests.yml b/ci_build/azure_pipelines/keras2onnx_application_tests.yml index df15f0e8e..47d55f2b4 100644 --- a/ci_build/azure_pipelines/keras2onnx_application_tests.yml +++ b/ci_build/azure_pipelines/keras2onnx_application_tests.yml @@ -8,8 +8,8 @@ jobs: vmImage: 'ubuntu-latest' strategy: matrix: - Python36-onnx1.5: - python.version: '3.6' + Python37-onnx1.5: + python.version: '3.7' ONNX_PATH: onnx==1.5.0 INSTALL_KERAS: pip install keras==2.2.4 UNINSTALL_KERAS: @@ -79,8 +79,8 @@ jobs: vmImage: 'windows-2019' strategy: matrix: - Python36-onnx1.5: - python.version: '3.6' + Python37-onnx1.5: + python.version: '3.7' ONNX_PATH: onnx==1.5.0 INSTALL_KERAS: pip install keras==2.2.4 UNINSTALL_KERAS: diff --git a/ci_build/azure_pipelines/keras2onnx_unit_test.yml b/ci_build/azure_pipelines/keras2onnx_unit_test.yml index 98ed7abc7..a812543f2 100644 --- a/ci_build/azure_pipelines/keras2onnx_unit_test.yml +++ b/ci_build/azure_pipelines/keras2onnx_unit_test.yml @@ -9,8 +9,8 @@ jobs: matrix: ############ TF Keras Unit Tests ############ - Python36-tf1.15: - python.version: '3.6' + Python37-tf1.15: + python.version: '3.7' ONNX_PATH: onnx==1.10.2 TENSORFLOW_PATH: tensorflow==1.15.0 INSTALL_ORT: pip install onnxruntime==1.9.0 @@ -45,8 +45,8 @@ jobs: INSTALL_NUMPY: pip install numpy==1.19.0 ############ Pure Keras Unit Tests ############ - Keras-Py36-tf1.15.0: - python.version: '3.6' + Keras-Py37-tf1.15.0: + python.version: '3.7' ONNX_PATH: onnx==1.10.2 KERAS: keras==2.2.5 TENSORFLOW_PATH: tensorflow==1.15.0 @@ -88,8 +88,8 @@ jobs: strategy: matrix: ############ TF Keras Unit Tests ############ - Python36-tf-1.15: - python.version: '3.6' + Python37-tf-1.15: + python.version: '3.7' ONNX_PATH: onnx==1.10.2 TENSORFLOW_PATH: tensorflow==1.15.0 INSTALL_ORT: pip install onnxruntime==1.9.0 @@ -123,8 +123,8 @@ jobs: INSTALL_NUMPY: pip install numpy==1.19.0 ############ Pure Keras Unit Tests ############ - Keras-Py36-tf1.15.0: - python.version: '3.6' + Keras-Py37-tf1.15.0: + python.version: '3.7' ONNX_PATH: onnx==1.10.2 KERAS: keras==2.2.5 TENSORFLOW_PATH: tensorflow==1.15.0 diff --git a/ci_build/azure_pipelines/onnxruntime_nightly_test.yml b/ci_build/azure_pipelines/onnxruntime_nightly_test.yml index e556677a2..8d75e5138 100644 --- a/ci_build/azure_pipelines/onnxruntime_nightly_test.yml +++ b/ci_build/azure_pipelines/onnxruntime_nightly_test.yml @@ -19,20 +19,8 @@ stages: - template: 'templates/job_generator.yml' parameters: platforms: ['linux', 'windows'] - python_versions: ['3.7', '3.6'] - tf_versions: ['1.13.1'] - onnx_opsets: [''] - onnx_backends: {onnxruntime: ['nightly']} - job: - steps: - - template: 'unit_test.yml' - report_coverage: 'True' - - - template: 'templates/job_generator.yml' - parameters: - platforms: ['linux', 'windows'] - python_versions: [3.7', '3.6'] - tf_versions: ['1.14.0'] + python_versions: ['3.7'] + tf_versions: ['1.13.1', '1.14.0'] onnx_opsets: [''] onnx_backends: {onnxruntime: ['nightly']} job: diff --git a/ci_build/azure_pipelines/pretrained_model_test-matrix.yml b/ci_build/azure_pipelines/pretrained_model_test-matrix.yml index 1d712ddf5..e0fd83d0e 100755 --- a/ci_build/azure_pipelines/pretrained_model_test-matrix.yml +++ b/ci_build/azure_pipelines/pretrained_model_test-matrix.yml @@ -4,17 +4,8 @@ jobs: - template: 'templates/job_generator.yml' parameters: platforms: ['linux', 'windows'] - python_versions: ['3.6'] - tf_versions: ['1.13.1', '1.12.3'] - job: - steps: - - template: 'pretrained_model_test.yml' - -- template: 'templates/job_generator.yml' - parameters: - platforms: ['linux', 'windows'] - python_versions: ['3.7', '3.6'] - tf_versions: ['1.14.0'] + python_versions: ['3.7'] + tf_versions: ['1.13.1', '1.14.0'] job: steps: - template: 'pretrained_model_test.yml' diff --git a/ci_build/azure_pipelines/templates/combine_test_coverage.yml b/ci_build/azure_pipelines/templates/combine_test_coverage.yml index 3639ad472..f31b25c3b 100644 --- a/ci_build/azure_pipelines/templates/combine_test_coverage.yml +++ b/ci_build/azure_pipelines/templates/combine_test_coverage.yml @@ -24,7 +24,7 @@ stages: inputs: createCustomEnvironment: 'true' environmentName: 'tf2onnx' - packageSpecs: 'python=3.6' + packageSpecs: 'python=3.7' updateConda: 'false' - bash: | diff --git a/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml b/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml index 809dfc710..8447a7d72 100644 --- a/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml +++ b/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml @@ -8,8 +8,8 @@ jobs: vmImage: 'ubuntu-latest' strategy: matrix: - Python36-onnx1.10: - python.version: '3.6' + Python37-onnx1.10: + python.version: '3.7' ONNX_PATH: onnx==1.10.2 INSTALL_KERAS: pip install keras==2.2.4 UNINSTALL_KERAS: diff --git a/ci_build/azure_pipelines/trimmed_keras2onnx_unit_test.yml b/ci_build/azure_pipelines/trimmed_keras2onnx_unit_test.yml index 45228b80c..4ad174fa4 100644 --- a/ci_build/azure_pipelines/trimmed_keras2onnx_unit_test.yml +++ b/ci_build/azure_pipelines/trimmed_keras2onnx_unit_test.yml @@ -9,11 +9,12 @@ jobs: matrix: ############ TF Keras Unit Tests ############ - Python36-tf1.15: - python.version: '3.6' + Python37-tf1.15: + python.version: '3.7' ONNX_PATH: onnx==1.10.2 TENSORFLOW_PATH: tensorflow==1.15.0 INSTALL_ORT: pip install onnxruntime==1.9.0 + INSTALL_NUMPY: pip install numpy==1.19.0 Python38-tf2.5: python.version: '3.8' @@ -51,11 +52,12 @@ jobs: strategy: matrix: ############ TF Keras Unit Tests ############ - Python36-tf-1.15: - python.version: '3.6' + Python37-tf-1.15: + python.version: '3.7' ONNX_PATH: onnx==1.10.2 TENSORFLOW_PATH: tensorflow==1.15.0 INSTALL_ORT: pip install onnxruntime==1.9.0 + INSTALL_NUMPY: pip install numpy==1.19.0 Python37-tf2.3: python.version: '3.7' diff --git a/ci_build/azure_pipelines/unit_test-matrix.yml b/ci_build/azure_pipelines/unit_test-matrix.yml index 19f5c29c3..3d05e319b 100644 --- a/ci_build/azure_pipelines/unit_test-matrix.yml +++ b/ci_build/azure_pipelines/unit_test-matrix.yml @@ -6,8 +6,8 @@ stages: - template: 'templates/job_generator.yml' parameters: platforms: ['linux', 'windows'] - python_versions: ['3.6'] - tf_versions: ['1.12.3'] + python_versions: ['3.7'] + tf_versions: ['1.13.1'] onnx_opsets: [''] job: steps: @@ -17,7 +17,7 @@ stages: - template: 'templates/job_generator.yml' parameters: platforms: ['linux', 'windows'] - python_versions: ['3.7', '3.6'] + python_versions: ['3.7'] tf_versions: ['1.14.0'] onnx_opsets: [''] job: @@ -61,7 +61,7 @@ stages: - template: 'templates/job_generator.yml' parameters: platforms: ['linux', 'windows'] - python_versions: ['3.8'] + python_versions: ['3.9'] tf_versions: ['2.8.0'] onnx_opsets: [''] job: diff --git a/ci_build/azure_pipelines/unit_test.yml b/ci_build/azure_pipelines/unit_test.yml index 0cc9d8a0a..cbcc39e37 100644 --- a/ci_build/azure_pipelines/unit_test.yml +++ b/ci_build/azure_pipelines/unit_test.yml @@ -127,9 +127,9 @@ stages: - template: 'templates/job_generator.yml' parameters: - # tf 1.12 - python_versions: [3.6'] - tf_versions: ['1.12.3'] + # tf 1.13 + python_versions: [3.7'] + tf_versions: ['1.13.1'] onnx_opsets: ['9'] job: steps: @@ -157,5 +157,16 @@ stages: - template: 'unit_test.yml' report_coverage: 'True' + - template: 'templates/job_generator.yml' + parameters: + python_versions: ['3.9'] + platforms: ['windows'] + tf_versions: ['2.8.1'] + onnx_opsets: ['15'] + job: + steps: + - template: 'unit_test.yml' + report_coverage: 'True' + - template: 'templates/combine_test_coverage.yml' diff --git a/setup.py b/setup.py index ae654b152..e4d5e2e52 100644 --- a/setup.py +++ b/setup.py @@ -95,7 +95,6 @@ def run(self): 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9'] From d1993a7346d52c3c63670e73bd0498f7cd8fde44 Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Thu, 19 May 2022 15:48:43 +0800 Subject: [PATCH 06/40] Add opset 16 support and check ci (#1937) * add opset 16 support and ci * python 3.7 requires tf>=1.13 Signed-off-by: Deyu Huang --- ci_build/azure_pipelines/templates/job_generator.yml | 4 ++-- ci_build/azure_pipelines/templates/setup.yml | 10 +++++++++- ci_build/azure_pipelines/templates/unit_test.yml | 2 +- tests/run_pretrained_models.yaml | 4 ++-- tf2onnx/constants.py | 3 ++- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/ci_build/azure_pipelines/templates/job_generator.yml b/ci_build/azure_pipelines/templates/job_generator.yml index 79851632e..7859f5991 100644 --- a/ci_build/azure_pipelines/templates/job_generator.yml +++ b/ci_build/azure_pipelines/templates/job_generator.yml @@ -5,8 +5,8 @@ parameters: python_versions: ['3.7'] tf_versions: [''] onnx_versions: [''] - onnx_opsets: ['15', '14', '13', '12', '11', '10', '9'] - onnx_backends: {onnxruntime: ['1.10.0']} + onnx_opsets: ['16', '15', '14', '13', '12', '11', '10', '9'] + onnx_backends: {onnxruntime: ['1.11.0']} job: {} run_setup: 'True' report_coverage: 'False' diff --git a/ci_build/azure_pipelines/templates/setup.yml b/ci_build/azure_pipelines/templates/setup.yml index 0f9e277ec..539f37964 100644 --- a/ci_build/azure_pipelines/templates/setup.yml +++ b/ci_build/azure_pipelines/templates/setup.yml @@ -4,14 +4,22 @@ steps: - bash: | set -ex pip install pytest pytest-cov pytest-runner coverage graphviz requests pyyaml pillow pandas parameterized - pip install $(CI_PIP_TF_NAME) $(CI_PIP_ONNX_NAME) $(CI_PIP_ONNX_BACKEND_NAME) + pip install $(CI_PIP_TF_NAME) $(CI_PIP_ONNX_NAME) + # TF < 2.7 reuires numpy <= 1.19, but onnxruntime >= 1.11 requires numpy >= 1.21 + if [[ $CI_TF_VERSION < 2.7 ]] && [[ $CI_ONNX_BACKEND == "onnxruntime" ]] ; + then + pip install $(CI_PIP_ONNX_BACKEND_NAME) numpy --no-deps -U + else + pip install $(CI_PIP_ONNX_BACKEND_NAME) + fi # TF 1.10 requires numpy <=1.14.5 and >=1.13.3, but onnxruntime 0.2.1 does not work with numpy <= 1.14.5 # Upgrade numpy only within constraints from other packages if any. if [[ $CI_TF_VERSION == 1.10* ]] && [[ $CI_ONNX_BACKEND == "onnxruntime" ]] ; then pip install $(CI_PIP_ONNX_NAME) $(CI_PIP_ONNX_BACKEND_NAME) numpy --no-deps -U fi + if [[ $CI_ONNXRUNTIME_NIGHTLY == "true" ]] ; then pip uninstall -y onnxruntime diff --git a/ci_build/azure_pipelines/templates/unit_test.yml b/ci_build/azure_pipelines/templates/unit_test.yml index 8dd67e3e6..b9fdd033a 100644 --- a/ci_build/azure_pipelines/templates/unit_test.yml +++ b/ci_build/azure_pipelines/templates/unit_test.yml @@ -1,7 +1,7 @@ # Run unit test parameters: - onnx_opsets: ['15', '14', '13', '12', '11', '10', '9', '8'] + onnx_opsets: ['16', '15', '14', '13', '12', '11', '10', '9'] skip_tflite_tests: 'True' skip_tfjs_tests: 'True' skip_tf_tests: 'False' diff --git a/tests/run_pretrained_models.yaml b/tests/run_pretrained_models.yaml index 14d0acfb7..3a195cf51 100644 --- a/tests/run_pretrained_models.yaml +++ b/tests/run_pretrained_models.yaml @@ -337,7 +337,7 @@ ssd_mobilenet_v3_large_coco: opset_constraints: "onnx": "min": 10 - "max": 15 + "max": 16 input_get: get_beach inputs: "normalized_input_image_tensor:0": [1, 320, 320, 3] @@ -432,7 +432,7 @@ faster_rcnn_inception_v2_coco: opset_constraints: "onnx": "min": 11 - "max": 15 + "max": 16 input_get: get_beach inputs: "image_tensor:0": [1, 224, 224, 3] diff --git a/tf2onnx/constants.py b/tf2onnx/constants.py index d5ce1fad4..1552824bf 100644 --- a/tf2onnx/constants.py +++ b/tf2onnx/constants.py @@ -50,6 +50,7 @@ # Mapping opset to IR version. # Note: opset 7 and opset 8 came out with IR3 but we need IR4 because of PlaceholderWithDefault +# Refer from https://github.com/onnx/onnx/blob/main/docs/Versioning.md#released-versions OPSET_TO_IR_VERSION = { - 1: 3, 2: 3, 3: 3, 4: 3, 5: 3, 6: 3, 7: 4, 8: 4, 9: 4, 10: 5, 11: 6, 12: 7, 13: 7, 14: 7, 15: 8 + 1: 3, 2: 3, 3: 3, 4: 3, 5: 3, 6: 3, 7: 4, 8: 4, 9: 4, 10: 5, 11: 6, 12: 7, 13: 7, 14: 7, 15: 8, 16: 8 } From 880754ed87c0b632e1e27a4e64d8cc4701dbceae Mon Sep 17 00:00:00 2001 From: Jay Zhang <36183870+fatcat-z@users.noreply.github.com> Date: Wed, 25 May 2022 11:07:58 +0800 Subject: [PATCH 07/40] Update the default opset version for tf2onnx. (#1946) * Update the default opset version for tf2onnx. * Update README.md accordingly. Signed-off-by: Jay Zhang --- README.md | 10 +++++----- tf2onnx/constants.py | 7 +++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 08fbef5b3..c16a42fe7 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The common issues we run into we try to document here [Troubleshooting Guide](Tr tf2onnx will use the ONNX version installed on your system and installs the latest ONNX version if none is found. We support and test ONNX opset-9 to opset-15. opset-6 to opset-8 should work but we don't test them. -By default we use ```opset-9``` for the resulting ONNX graph since most runtimes will support opset-9. +By default we use ```opset-13``` for the resulting ONNX graph. If you want the graph to be generated with a specific opset, use ```--opset``` in the command line, for example ```--opset 13```. @@ -98,9 +98,9 @@ To get started with `tensorflow-onnx`, run the `t2onnx.convert` command, providi ```python -m tf2onnx.convert --saved-model tensorflow-model-path --output model.onnx``` -The above command uses a default of `9` for the ONNX opset. If you need a newer opset, or want to limit your model to use an older opset then you can provide the `--opset` argument to the command. If you are unsure about which opset to use, refer to the [ONNX operator documentation](https://github.com/onnx/onnx/releases). +The above command uses a default of `13` for the ONNX opset. If you need a newer opset, or want to limit your model to use an older opset then you can provide the `--opset` argument to the command. If you are unsure about which opset to use, refer to the [ONNX operator documentation](https://github.com/onnx/onnx/releases). -```python -m tf2onnx.convert --saved-model tensorflow-model-path --opset 13 --output model.onnx``` +```python -m tf2onnx.convert --saved-model tensorflow-model-path --opset 15 --output model.onnx``` If your TensorFlow model is in a format other than `saved model`, then you need to provide the inputs and outputs of the model graph. @@ -118,7 +118,7 @@ You find an end-to-end tutorial for ssd-mobilenet [here](tutorials/ConvertingSSD We recently added support for tflite. You convert ```tflite``` models via command line, for example: -```python -m tf2onnx.convert --opset 13 --tflite tflite--file --output model.onnx``` +```python -m tf2onnx.convert --opset 15 --tflite tflite--file --output model.onnx``` ## CLI reference @@ -187,7 +187,7 @@ ONNX requires default values for graph inputs to be constant, while Tensorflow's #### --opset -By default we use the opset 9 to generate the graph. By specifying ```--opset``` the user can override the default to generate a graph with the desired opset. For example ```--opset 13``` would create a onnx graph that uses only ops available in opset 13. Because older opsets have in most cases fewer ops, some models might not convert on a older opset. +By default we use the opset 13 to generate the graph. By specifying ```--opset``` the user can override the default to generate a graph with the desired opset. For example ```--opset 15``` would create a onnx graph that uses only ops available in opset 15. Because older opsets have in most cases fewer ops, some models might not convert on a older opset. #### --dequantize diff --git a/tf2onnx/constants.py b/tf2onnx/constants.py index 1552824bf..9a7005453 100644 --- a/tf2onnx/constants.py +++ b/tf2onnx/constants.py @@ -15,8 +15,11 @@ MICROSOFT_DOMAIN = "com.microsoft" CONTRIB_OPS_DOMAIN = "ai.onnx.contrib" -# Default opset version for onnx domain -PREFERRED_OPSET = 9 +# Default opset version for onnx domain. +# The current update policy is that the default should be set to +# the latest released version as of 18 months ago. +# Opset 13 was released in ONNX v1.8.0 (Nov, 2020). +PREFERRED_OPSET = 13 # Default opset for custom ops TENSORFLOW_OPSET = helper.make_opsetid("ai.onnx.converters.tensorflow", 1) From e099356bc8b7ceedec0e99a101de199a5deccfe4 Mon Sep 17 00:00:00 2001 From: Jay Zhang <36183870+fatcat-z@users.noreply.github.com> Date: Wed, 25 May 2022 17:10:46 +0800 Subject: [PATCH 08/40] Update the way to check input_signature in from_function(). (#1947) * Update the way to check input_signature in from_function(). Signed-off-by: Jay Zhang --- tests/test_api.py | 17 +++++++++++++++++ tf2onnx/convert.py | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/test_api.py b/tests/test_api.py index 3bf170f03..e81d73946 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -173,6 +173,23 @@ def func(foo, a, x, b, w): res_onnx = self.run_onnxruntime(output_path, {"x": x, "w": w}, output_names) self.assertAllClose(res_tf, res_onnx[0], rtol=1e-5, atol=1e-5) + @check_tf_min_version("2.0") + def test_function_nparray(self): + @tf.function + def func(x): + return tf.math.sqrt(x) + + output_path = os.path.join(self.test_data_directory, "model.onnx") + x = np.asarray([1.0, 2.0]) + + res_tf = func(x) + spec = np.asarray([[1.0, 2.0]]) + model_proto, _ = tf2onnx.convert.from_function(func, input_signature=spec, + opset=self.config.opset, output_path=output_path) + output_names = [n.name for n in model_proto.graph.output] + res_onnx = self.run_onnxruntime(output_path, {'x': x}, output_names) + self.assertAllClose(res_tf, res_onnx[0], rtol=1e-5, atol=1e-5) + @check_tf_min_version("1.15") def _test_graphdef(self): def func(x, y): diff --git a/tf2onnx/convert.py b/tf2onnx/convert.py index bdf7df58f..455224a12 100644 --- a/tf2onnx/convert.py +++ b/tf2onnx/convert.py @@ -535,7 +535,7 @@ def from_function(function, input_signature=None, opset=None, custom_ops=None, c if LooseVersion(tf.__version__) < "2.0": raise NotImplementedError("from_function requires tf-2.0 or newer") - if not input_signature: + if input_signature is None: raise ValueError("from_function requires input_signature") concrete_func = function.get_concrete_function(*input_signature) From aaab800676b598a16ac92365d9f658c05127a0d2 Mon Sep 17 00:00:00 2001 From: Jay Zhang <36183870+fatcat-z@users.noreply.github.com> Date: Thu, 26 May 2022 11:20:15 +0800 Subject: [PATCH 09/40] Add mapping for tf lite op TFL_BATCH_MATMUL and TFL_MATMUL. (#1950) Signed-off-by: Jay Zhang --- tf2onnx/tflite_handlers/tfl_direct.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tf2onnx/tflite_handlers/tfl_direct.py b/tf2onnx/tflite_handlers/tfl_direct.py index d38aa8227..c79f22811 100644 --- a/tf2onnx/tflite_handlers/tfl_direct.py +++ b/tf2onnx/tflite_handlers/tfl_direct.py @@ -12,6 +12,7 @@ @tfl_op("TFL_ABS", tf_op="Abs") +@tfl_op("TFL_BATCH_MATMUL", tf_op="BatchMatMul") @tfl_op("TFL_BROADCAST_TO", tf_op="BroadcastTo") @tfl_op("TFL_CEIL", tf_op="Ceil") @tfl_op("TFL_COS", tf_op="Cos") @@ -30,6 +31,7 @@ @tfl_op("TFL_LOGICAL_AND", tf_op="LogicalAnd") @tfl_op("TFL_LOGICAL_NOT", tf_op="LogicalNot") @tfl_op("TFL_LOGICAL_OR", tf_op="LogicalOr") +@tfl_op("TFL_MATMUL", tf_op="MatMul") @tfl_op("TFL_MATRIX_DIAG", tf_op="MatrixDiag") @tfl_op("TFL_MATRIX_SET_DIAG", tf_op="MatrixSetDiag") @tfl_op("TFL_MAXIMUM", tf_op="Maximum") From 6f5a673e98b2982b14d28cbf1c352893fc061497 Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Fri, 27 May 2022 09:46:42 +0800 Subject: [PATCH 10/40] Update protobuf version in ci (#1951) * update protobuf version in ci Signed-off-by: Deyu Huang * add change in unit_test Signed-off-by: Deyu Huang --- .../templates/keras2onnx_application_tests.yml | 4 +++- ci_build/azure_pipelines/templates/keras2onnx_unit_test.yml | 4 +++- ci_build/azure_pipelines/templates/setup.yml | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ci_build/azure_pipelines/templates/keras2onnx_application_tests.yml b/ci_build/azure_pipelines/templates/keras2onnx_application_tests.yml index 8d5530963..23b2e6585 100644 --- a/ci_build/azure_pipelines/templates/keras2onnx_application_tests.yml +++ b/ci_build/azure_pipelines/templates/keras2onnx_application_tests.yml @@ -19,6 +19,8 @@ steps: python -m pip install --upgrade pip conda config --set always_yes yes --set changeps1 no pip install $(ONNX_PATH) + pip uninstall -y protobuf + pip install "protobuf<4.21.0" pip install h5py==2.9.0 pip install parameterized $(INSTALL_TENSORFLOW) @@ -81,7 +83,7 @@ steps: echo Test numpy installation... && python -c "import numpy" pip install %ONNX_PATH% pip uninstall -y protobuf - pip install protobuf + pip install "protobuf<4.21.0" pip install h5py==2.9.0 pip install parameterized %INSTALL_TENSORFLOW% diff --git a/ci_build/azure_pipelines/templates/keras2onnx_unit_test.yml b/ci_build/azure_pipelines/templates/keras2onnx_unit_test.yml index 43f1d7688..ecf40053c 100644 --- a/ci_build/azure_pipelines/templates/keras2onnx_unit_test.yml +++ b/ci_build/azure_pipelines/templates/keras2onnx_unit_test.yml @@ -19,6 +19,8 @@ steps: python -m pip install --upgrade pip conda config --set always_yes yes --set changeps1 no pip install $(ONNX_PATH) + pip uninstall -y protobuf + pip install "protobuf<4.21.0" pip install h5py==2.9.0 pip install parameterized pip install $(TENSORFLOW_PATH) @@ -71,7 +73,7 @@ steps: echo Test numpy installation... && python -c "import numpy" pip install %ONNX_PATH% pip uninstall -y protobuf - pip install protobuf + pip install "protobuf<4.21.0" pip install h5py==2.9.0 pip install parameterized pip install %TENSORFLOW_PATH% diff --git a/ci_build/azure_pipelines/templates/setup.yml b/ci_build/azure_pipelines/templates/setup.yml index 539f37964..cc6d4bc3d 100644 --- a/ci_build/azure_pipelines/templates/setup.yml +++ b/ci_build/azure_pipelines/templates/setup.yml @@ -6,6 +6,10 @@ steps: pip install pytest pytest-cov pytest-runner coverage graphviz requests pyyaml pillow pandas parameterized pip install $(CI_PIP_TF_NAME) $(CI_PIP_ONNX_NAME) + # protobuf release version 4.21.0 has some Python changes which is not compatible with tensorflow so far. + pip uninstall -y protobuf + pip install "protobuf<4.21.0" + # TF < 2.7 reuires numpy <= 1.19, but onnxruntime >= 1.11 requires numpy >= 1.21 if [[ $CI_TF_VERSION < 2.7 ]] && [[ $CI_ONNX_BACKEND == "onnxruntime" ]] ; then From 29b76dfdf9f5ded96d9c77a1bc9b84cdc0dbac0f Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Fri, 27 May 2022 16:17:15 +0800 Subject: [PATCH 11/40] Add TensorScatterAdd op for opset 16 (#1949) * add TensorScatterAdd op conversion in opset 16 Signed-off-by: Deyu Huang * add unit test Signed-off-by: Deyu Huang * fix pylint name Signed-off-by: Deyu Huang * fix typo and test op check Signed-off-by: Deyu Huang Co-authored-by: Jay Zhang Co-authored-by: Jay Zhang --- tests/test_backend.py | 11 +++++++++++ tf2onnx/onnx_opset/tensor.py | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index 144f7ca76..c0e822a77 100755 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -4698,6 +4698,17 @@ def func(x, y, z): return tf.identity(x_, name=_TFOUTPUT) self._run_test_case(func, [_OUTPUT], {_INPUT: x_val, _INPUT1: y_val, _INPUT2: z_val}) + @check_opset_min_version(16, "ScatterND") + def test_scatternd_add(self): + x_val = np.array([10, 20, 30, 40], dtype=np.int32).reshape((4)) + y_val = np.array([0, 2], dtype=np.int64).reshape((2, 1)) + z_val = np.array([20, 30], dtype=np.int32).reshape((2)) + + def func(x, y, z): + x_ = tf.tensor_scatter_nd_add(x, y, z) + return tf.identity(x_, name=_TFOUTPUT) + self._run_test_case(func, [_OUTPUT], {_INPUT: x_val, _INPUT1: y_val, _INPUT2: z_val}) + @check_opset_min_version(11, "ScatterND") def test_scatternd_1d(self): x_val = np.array([4, 3, 1, 7], dtype=np.int32).reshape((4, 1)) diff --git a/tf2onnx/onnx_opset/tensor.py b/tf2onnx/onnx_opset/tensor.py index 49564223e..9ad2b513e 100644 --- a/tf2onnx/onnx_opset/tensor.py +++ b/tf2onnx/onnx_opset/tensor.py @@ -655,6 +655,16 @@ def version_11(cls, ctx, node, **kwargs): ctx.replace_inputs(node, [node.input[2], node.input[0], node.input[1]]) +@tf_op("TensorScatterAdd", onnx_op="ScatterND") +class TensorScatterAdd: + @classmethod + def version_16(cls, ctx, node, **kwargs): + # indicies input must be int64 in ONNX. + if ctx.get_dtype(node.input[1]) != TensorProto.INT64: + ctx.insert_new_node_on_input(node, "Cast", node.input[1], to=TensorProto.INT64) + node.set_attr("reduction", 'add') + + @tf_op("TensorScatterUpdate", onnx_op="ScatterND") class TensorScatterUpdate: @classmethod From 16eb4b4ed58b4b1f3b439cae0178cd0705675a45 Mon Sep 17 00:00:00 2001 From: Jay Zhang <36183870+fatcat-z@users.noreply.github.com> Date: Mon, 30 May 2022 21:05:12 +0800 Subject: [PATCH 12/40] Add a new api from_tflite to improve user experience. (#1954) * Add a new api from_tflite to improve user experience. Signed-off-by: Jay Zhang --- README.md | 30 ++++++++++ .../regression/tflite/test_api_model.tflite | Bin 0 -> 636 bytes tests/test_api.py | 13 ++++ tf2onnx/convert.py | 56 ++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 tests/models/regression/tflite/test_api_model.tflite diff --git a/README.md b/README.md index c16a42fe7..a3cb9dd2f 100644 --- a/README.md +++ b/README.md @@ -377,6 +377,36 @@ model_proto, external_tensor_storage = tf2onnx.convert.from_graph_def(graph_def, An ONNX model_proto and an external_tensor_storage dict. ``` +### from_tflite +``` +import tf2onnx + +model_proto, external_tensor_storage = tf2onnx.convert.from_tflite(tflite_path, + input_names=None, output_names=None, opset=None, custom_ops=None, custom_op_handlers=None, + custom_rewriter=None, inputs_as_nchw=None, extra_opset=None, shape_override=None, target=None, + large_model=False, output_path=None): + + Args: + tflite_path: the tflite model file full path + input_names: list of input names + output_names: list of output names + opset: the opset to be used for the ONNX model, default is the latest + custom_ops: if a model contains ops not recognized by onnx runtime, + you can tag these ops with a custom op domain so that the + runtime can still open the model. Type is a dictionary `{op name: domain}`. + custom_op_handlers: dictionary of custom ops handlers + custom_rewriter: list of custom graph rewriters + inputs_as_nchw: transpose inputs in list from nchw to nhwc + extra_opset: list of extra opset's, for example the opset's used by custom ops + shape_override: dict with inputs that override the shapes given by tensorflow + target: list of workarounds applied to help certain platforms + large_model: use the ONNX external tensor storage format + output_path: save model to output_path + + Returns: + An ONNX model_proto and an external_tensor_storage dict. +``` + ### Creating custom op mappings from python For complex custom ops that require graph rewrites or input / attribute rewrites using the python interface to insert a custom op will be the easiest way to accomplish the task. diff --git a/tests/models/regression/tflite/test_api_model.tflite b/tests/models/regression/tflite/test_api_model.tflite new file mode 100644 index 0000000000000000000000000000000000000000..017255a82376c5f62926f0c15963584551fd23f7 GIT binary patch literal 636 zcmY*X%Syvg5IxmcOIu>WE?jic50Fq_pvzDz7W#l1TnSNV7Fi^bnuuQ_E?l_uV?%-kGEx(_kfxbZGb*M&9Lv;}edwGy18Sgd z>egwA+aV5|lQPBw<>NQczld*Qhv(^Jyyy>;m@}lo(lK>)C%|5BSAA`I Date: Tue, 31 May 2022 13:07:16 +0800 Subject: [PATCH 13/40] Upgrade opset 16 version related doc (#1953) * upgrade opset 16 status doc Signed-off-by: Deyu Huang --- README.md | 12 +- support_status.md | 524 +++++++++++++++++++++++----------------------- tools/gen_doc.py | 2 +- 3 files changed, 270 insertions(+), 268 deletions(-) diff --git a/README.md b/README.md index a3cb9dd2f..806573a41 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ The common issues we run into we try to document here [Troubleshooting Guide](Tr | Build Type | OS | Python | TensorFlow | ONNX opset | Status | | --- | --- | --- | --- | --- | --- | -| Unit Test - Basic | Linux, MacOS\*, Windows\* | 3.7-3.9 | 1.13-1.15, 2.1-2.8 | 9-15 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=16&branchName=main) | -| Unit Test - Full | Linux, MacOS, Windows | 3.7-3.9 | 1.13-1.15, 2.1-2.8 | 9-15 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test-matrix?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=18&branchName=main) | | +| Unit Test - Basic | Linux, MacOS\*, Windows\* | 3.7-3.9 | 1.13-1.15, 2.1-2.8 | 9-16 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=16&branchName=main) | +| Unit Test - Full | Linux, MacOS, Windows | 3.7-3.9 | 1.13-1.15, 2.1-2.8 | 9-16 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test-matrix?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=18&branchName=main) | |
## Supported Versions @@ -27,7 +27,7 @@ The common issues we run into we try to document here [Troubleshooting Guide](Tr tf2onnx will use the ONNX version installed on your system and installs the latest ONNX version if none is found. -We support and test ONNX opset-9 to opset-15. opset-6 to opset-8 should work but we don't test them. +We support and test ONNX opset-9 to opset-16. opset-6 to opset-8 should work but we don't test them. By default we use ```opset-13``` for the resulting ONNX graph. If you want the graph to be generated with a specific opset, use ```--opset``` in the command line, for example ```--opset 13```. @@ -100,7 +100,7 @@ To get started with `tensorflow-onnx`, run the `t2onnx.convert` command, providi The above command uses a default of `13` for the ONNX opset. If you need a newer opset, or want to limit your model to use an older opset then you can provide the `--opset` argument to the command. If you are unsure about which opset to use, refer to the [ONNX operator documentation](https://github.com/onnx/onnx/releases). -```python -m tf2onnx.convert --saved-model tensorflow-model-path --opset 15 --output model.onnx``` +```python -m tf2onnx.convert --saved-model tensorflow-model-path --opset 16 --output model.onnx``` If your TensorFlow model is in a format other than `saved model`, then you need to provide the inputs and outputs of the model graph. @@ -118,7 +118,7 @@ You find an end-to-end tutorial for ssd-mobilenet [here](tutorials/ConvertingSSD We recently added support for tflite. You convert ```tflite``` models via command line, for example: -```python -m tf2onnx.convert --opset 15 --tflite tflite--file --output model.onnx``` +```python -m tf2onnx.convert --opset 16 --tflite tflite--file --output model.onnx``` ## CLI reference @@ -187,7 +187,7 @@ ONNX requires default values for graph inputs to be constant, while Tensorflow's #### --opset -By default we use the opset 13 to generate the graph. By specifying ```--opset``` the user can override the default to generate a graph with the desired opset. For example ```--opset 15``` would create a onnx graph that uses only ops available in opset 15. Because older opsets have in most cases fewer ops, some models might not convert on a older opset. +By default we use the opset 13 to generate the graph. By specifying ```--opset``` the user can override the default to generate a graph with the desired opset. For example ```--opset 16``` would create a onnx graph that uses only ops available in opset 16. Because older opsets have in most cases fewer ops, some models might not convert on a older opset. #### --dequantize diff --git a/support_status.md b/support_status.md index e66e6fd00..4586c3d12 100644 --- a/support_status.md +++ b/support_status.md @@ -4,267 +4,269 @@ ### Domain: "" (default domain) | Tensorflow Op | Convertible to ONNX Op Versions | | ------------- | ------------------------------- | -| Abs | 1 ~ 15 | -| Acos | 7 ~ 15 | -| Acosh | 9 ~ 15 | -| Add | 1 ~ 15 | -| AddN | 6 ~ 15 | -| AddV2 | 1 ~ 15 | -| AdjustContrastv2 | 1 ~ 15 | -| AdjustHue | 11 ~ 15 | -| AdjustSaturation | 11 ~ 15 | -| All | 6 ~ 15 | -| Any | 6 ~ 15 | -| ArgMax | 1 ~ 15 | -| ArgMin | 1 ~ 15 | -| AsString | 9 ~ 15 | -| Asin | 7 ~ 15 | -| Asinh | 9 ~ 15 | -| Atan | 7 ~ 15 | -| Atan2 | 9 ~ 15 | -| Atanh | 9 ~ 15 | -| AvgPool | 1 ~ 15 | -| AvgPool3D | 1 ~ 15 | -| BatchMatMul | 1 ~ 15 | -| BatchMatMulV2 | 1 ~ 15 | -| BatchToSpaceND | 1 ~ 15 | -| BiasAdd | 1 ~ 15 | -| BiasAddV1 | 1 ~ 15 | -| Bincount | 11 ~ 15 | -| BroadcastTo | 8 ~ 15 | -| CTCGreedyDecoder | 11 ~ 15 | -| Cast | 1 ~ 15 | -| Ceil | 1 ~ 15 | -| CheckNumerics | 1 ~ 15 | -| ClipByValue | 8 ~ 15 | -| CombinedNonMaxSuppression | 12 ~ 15 | -| ComplexAbs | 1 ~ 15 | -| Concat | 1 ~ 15 | -| ConcatV2 | 1 ~ 15 | -| Const | 1 ~ 15 | -| ConstV2 | 1 ~ 15 | -| Conv1D | 1 ~ 15 | -| Conv2D | 1 ~ 15 | -| Conv2DBackpropInput | 1 ~ 15 | -| Conv3D | 1 ~ 15 | -| Conv3DBackpropInputV2 | 1 ~ 15 | -| Cos | 7 ~ 15 | -| Cosh | 9 ~ 15 | -| CropAndResize | 10 ~ 15 | -| CudnnRNN | 10 ~ 15 | -| Cumsum | 11 ~ 15 | -| DenseBincount | 11 ~ 15 | -| DenseToDenseSetOperation | 11 ~ 15 | -| DepthToSpace | 1 ~ 15 | -| DepthwiseConv2d | 1 ~ 15 | -| DepthwiseConv2dNative | 1 ~ 15 | -| Div | 1 ~ 15 | -| DivNoNan | 9 ~ 15 | -| Dropout | 1 ~ 15 | -| DynamicPartition | 9 ~ 15 | -| DynamicStitch | 10 ~ 15 | -| Einsum | 12 ~ 15 | -| Elu | 1 ~ 15 | -| EnsureShape | 1 ~ 15 | -| Equal | 1 ~ 15 | -| Erf | 1 ~ 15 | -| Exp | 1 ~ 15 | -| ExpandDims | 1 ~ 15 | -| FFT | 1 ~ 15 | -| FIFOQueueV2 | 8 ~ 15 | -| FakeQuantWithMinMaxArgs | 10 ~ 15 | -| FakeQuantWithMinMaxVars | 10 ~ 15 | -| Fill | 7 ~ 15 | -| Flatten | 1 ~ 15 | -| Floor | 1 ~ 15 | -| FloorDiv | 6 ~ 15 | -| FloorMod | 7 ~ 15 | -| FusedBatchNorm | 6 ~ 15 | -| FusedBatchNormV2 | 6 ~ 15 | -| FusedBatchNormV3 | 6 ~ 15 | -| Gather | 1 ~ 15 | -| GatherNd | 1 ~ 15 | -| GatherV2 | 1 ~ 15 | -| Greater | 1 ~ 15 | -| GreaterEqual | 7 ~ 15 | -| HardSwish | 14 ~ 15 | -| HashTableV2 | 8 ~ 15 | -| Identity | 1 ~ 15 | -| IdentityN | 1 ~ 15 | -| If | 1 ~ 15 | -| InvertPermutation | 11 ~ 15 | -| IsFinite | 10 ~ 15 | -| IsInf | 10 ~ 15 | -| IsNan | 9 ~ 15 | -| IteratorGetNext | 8 ~ 15 | -| IteratorV2 | 8 ~ 15 | -| LRN | 1 ~ 15 | -| LSTMBlockCell | 1 ~ 15 | -| LeakyRelu | 1 ~ 15 | -| LeftShift | 11 ~ 15 | -| Less | 1 ~ 15 | -| LessEqual | 7 ~ 15 | -| Log | 1 ~ 15 | -| LogSoftmax | 1 ~ 15 | -| LogicalAnd | 1 ~ 15 | -| LogicalNot | 1 ~ 15 | -| LogicalOr | 1 ~ 15 | -| LookupTableFindV2 | 8 ~ 15 | -| LookupTableSizeV2 | 1 ~ 15 | -| Loop | 7 ~ 15 | -| MatMul | 1 ~ 15 | -| MatrixBandPart | 7 ~ 15 | -| MatrixDeterminant | 11 ~ 15 | -| MatrixDiag | 12 ~ 15 | -| MatrixDiagPart | 11 ~ 15 | -| MatrixDiagPartV2 | 11 ~ 15 | -| MatrixDiagPartV3 | 11 ~ 15 | -| MatrixDiagV2 | 12 ~ 15 | -| MatrixDiagV3 | 12 ~ 15 | -| MatrixSetDiagV3 | 12 ~ 15 | -| Max | 1 ~ 15 | -| MaxPool | 1 ~ 15 | -| MaxPool3D | 1 ~ 15 | -| MaxPoolV2 | 1 ~ 15 | -| MaxPoolWithArgmax | 8 ~ 15 | -| Maximum | 1 ~ 15 | -| Mean | 1 ~ 15 | -| Min | 1 ~ 15 | -| Minimum | 1 ~ 15 | -| MirrorPad | 1 ~ 15 | -| Mul | 1 ~ 15 | -| Multinomial | 7 ~ 15 | -| Neg | 1 ~ 15 | -| NoOp | 1 ~ 15 | -| NonMaxSuppressionV2 | 10 ~ 15 | -| NonMaxSuppressionV3 | 10 ~ 15 | -| NonMaxSuppressionV4 | 10 ~ 15 | -| NonMaxSuppressionV5 | 10 ~ 15 | -| NotEqual | 1 ~ 15 | -| OneHot | 1 ~ 15 | -| Pack | 1 ~ 15 | -| Pad | 1 ~ 15 | -| PadV2 | 1 ~ 15 | -| ParallelDynamicStitch | 10 ~ 15 | -| Placeholder | 1 ~ 15 | -| PlaceholderV2 | 1 ~ 15 | -| PlaceholderWithDefault | 1 ~ 15 | -| Pow | 1 ~ 15 | -| Prelu | 1 ~ 15 | -| Prod | 1 ~ 15 | -| QueueDequeueManyV2 | 8 ~ 15 | -| QueueDequeueUpToV2 | 8 ~ 15 | -| QueueDequeueV2 | 8 ~ 15 | -| RFFT | 1 ~ 15 | -| RFFT2D | 1 ~ 15 | -| RaggedGather | 11 ~ 15 | -| RaggedRange | 11 ~ 15 | -| RaggedTensorFromVariant | 13 ~ 15 | -| RaggedTensorToSparse | 11 ~ 15 | -| RaggedTensorToTensor | 11 ~ 15 | -| RaggedTensorToVariant | 13 ~ 15 | -| RandomNormal | 1 ~ 15 | -| RandomNormalLike | 1 ~ 15 | -| RandomShuffle | 10 ~ 15 | -| RandomStandardNormal | 1 ~ 15 | -| RandomUniform | 1 ~ 15 | -| RandomUniformInt | 1 ~ 15 | -| RandomUniformLike | 1 ~ 15 | -| Range | 7 ~ 15 | -| RealDiv | 1 ~ 15 | -| Reciprocal | 1 ~ 15 | -| Relu | 1 ~ 15 | -| Relu6 | 1 ~ 15 | -| Reshape | 1 ~ 15 | -| ResizeBicubic | 7 ~ 15 | -| ResizeBilinear | 7 ~ 15 | -| ResizeNearestNeighbor | 7 ~ 15 | -| ReverseSequence | 8 ~ 15 (Except 9) | -| ReverseV2 | 10 ~ 15 | -| RightShift | 11 ~ 15 | -| Roll | 10 ~ 15 | -| Round | 1 ~ 15 | -| Rsqrt | 1 ~ 15 | -| SampleDistortedBoundingBox | 9 ~ 15 | -| SampleDistortedBoundingBoxV2 | 9 ~ 15 | -| Scan | 7 ~ 15 | -| ScatterNd | 11 ~ 15 | -| SegmentMax | 11 ~ 15 | -| SegmentMean | 11 ~ 15 | -| SegmentMin | 11 ~ 15 | -| SegmentProd | 11 ~ 15 | -| SegmentSum | 11 ~ 15 | -| Select | 7 ~ 15 | -| SelectV2 | 7 ~ 15 | -| Selu | 1 ~ 15 | -| Shape | 1 ~ 15 | -| Sigmoid | 1 ~ 15 | -| Sign | 1 ~ 15 | -| Sin | 7 ~ 15 | -| Sinh | 9 ~ 15 | -| Size | 1 ~ 15 | -| Slice | 1 ~ 15 | -| Softmax | 1 ~ 15 | -| SoftmaxCrossEntropyWithLogits | 7 ~ 15 | -| Softplus | 1 ~ 15 | -| Softsign | 1 ~ 15 | -| SpaceToBatchND | 1 ~ 15 | -| SpaceToDepth | 1 ~ 15 | -| SparseFillEmptyRows | 11 ~ 15 | -| SparseReshape | 11 ~ 15 | -| SparseSegmentMean | 11 ~ 15 | -| SparseSegmentMeanWithNumSegments | 11 ~ 15 | -| SparseSegmentSqrtN | 11 ~ 15 | -| SparseSegmentSqrtNWithNumSegments | 11 ~ 15 | -| SparseSegmentSum | 11 ~ 15 | -| SparseSegmentSumWithNumSegments | 11 ~ 15 | -| SparseSoftmaxCrossEntropyWithLogits | 7 ~ 15 | -| SparseToDense | 11 ~ 15 | -| Split | 1 ~ 15 | -| SplitV | 1 ~ 15 | -| Sqrt | 1 ~ 15 | -| Square | 1 ~ 15 | -| SquaredDifference | 1 ~ 15 | -| SquaredDistance | 12 ~ 15 | -| Squeeze | 1 ~ 15 | -| StatelessIf | 1 ~ 15 | -| StatelessWhile | 7 ~ 15 | -| StopGradient | 1 ~ 15 | -| StridedSlice | 1 ~ 15 | -| StringLower | 10 ~ 15 | -| StringToNumber | 9 ~ 15 | -| StringUpper | 10 ~ 15 | -| Sub | 1 ~ 15 | -| Sum | 1 ~ 15 | -| TFL_CONCATENATION | 1 ~ 15 | -| TFL_DEQUANTIZE | 1 ~ 15 | -| TFL_PRELU | 7 ~ 15 | -| TFL_QUANTIZE | 1 ~ 15 | -| TFL_TFLite_Detection_PostProcess | 11 ~ 15 | -| TFL_WHILE | 7 ~ 15 | -| Tan | 7 ~ 15 | -| Tanh | 1 ~ 15 | -| TensorListFromTensor | 7 ~ 15 | -| TensorListGetItem | 7 ~ 15 | -| TensorListLength | 7 ~ 15 | -| TensorListReserve | 7 ~ 15 | -| TensorListResize | 7 ~ 15 | -| TensorListSetItem | 7 ~ 15 | -| TensorListStack | 7 ~ 15 | -| TensorScatterUpdate | 11 ~ 15 | -| Tile | 1 ~ 15 | -| TopKV2 | 1 ~ 15 | -| Transpose | 1 ~ 15 | -| TruncateDiv | 1 ~ 15 | -| Unique | 11 ~ 15 | -| Unpack | 1 ~ 15 | -| UnsortedSegmentMax | 11 ~ 15 | -| UnsortedSegmentMin | 11 ~ 15 | -| UnsortedSegmentProd | 11 ~ 15 | -| UnsortedSegmentSum | 11 ~ 15 | -| Where | 9 ~ 15 | -| While | 7 ~ 15 | -| ZerosLike | 1 ~ 15 | +| Abs | 1 ~ 16 | +| Acos | 7 ~ 16 | +| Acosh | 9 ~ 16 | +| Add | 1 ~ 16 | +| AddN | 6 ~ 16 | +| AddV2 | 1 ~ 16 | +| AdjustContrastv2 | 1 ~ 16 | +| AdjustHue | 11 ~ 16 | +| AdjustSaturation | 11 ~ 16 | +| All | 6 ~ 16 | +| Any | 6 ~ 16 | +| ArgMax | 1 ~ 16 | +| ArgMin | 1 ~ 16 | +| AsString | 9 ~ 16 | +| Asin | 7 ~ 16 | +| Asinh | 9 ~ 16 | +| Atan | 7 ~ 16 | +| Atan2 | 9 ~ 16 | +| Atanh | 9 ~ 16 | +| AvgPool | 1 ~ 16 | +| AvgPool3D | 1 ~ 16 | +| BatchMatMul | 1 ~ 16 | +| BatchMatMulV2 | 1 ~ 16 | +| BatchToSpaceND | 1 ~ 16 | +| BiasAdd | 1 ~ 16 | +| BiasAddV1 | 1 ~ 16 | +| Bincount | 11 ~ 16 | +| BroadcastTo | 8 ~ 16 | +| CTCGreedyDecoder | 11 ~ 16 | +| Cast | 1 ~ 16 | +| Ceil | 1 ~ 16 | +| CheckNumerics | 1 ~ 16 | +| ClipByValue | 8 ~ 16 | +| CombinedNonMaxSuppression | 12 ~ 16 | +| ComplexAbs | 1 ~ 16 | +| Concat | 1 ~ 16 | +| ConcatV2 | 1 ~ 16 | +| Const | 1 ~ 16 | +| ConstV2 | 1 ~ 16 | +| Conv1D | 1 ~ 16 | +| Conv2D | 1 ~ 16 | +| Conv2DBackpropInput | 1 ~ 16 | +| Conv3D | 1 ~ 16 | +| Conv3DBackpropInputV2 | 1 ~ 16 | +| Cos | 7 ~ 16 | +| Cosh | 9 ~ 16 | +| CropAndResize | 10 ~ 16 | +| CudnnRNN | 10 ~ 16 | +| Cumsum | 11 ~ 16 | +| DenseBincount | 11 ~ 16 | +| DenseToDenseSetOperation | 11 ~ 16 | +| DepthToSpace | 1 ~ 16 | +| DepthwiseConv2d | 1 ~ 16 | +| DepthwiseConv2dNative | 1 ~ 16 | +| Div | 1 ~ 16 | +| DivNoNan | 9 ~ 16 | +| Dropout | 1 ~ 16 | +| DynamicPartition | 9 ~ 16 | +| DynamicStitch | 10 ~ 16 | +| Einsum | 12 ~ 16 | +| Elu | 1 ~ 16 | +| EnsureShape | 1 ~ 16 | +| Equal | 1 ~ 16 | +| Erf | 1 ~ 16 | +| Exp | 1 ~ 16 | +| ExpandDims | 1 ~ 16 | +| FFT | 1 ~ 16 | +| FIFOQueueV2 | 8 ~ 16 | +| FakeQuantWithMinMaxArgs | 10 ~ 16 | +| FakeQuantWithMinMaxVars | 10 ~ 16 | +| Fill | 7 ~ 16 | +| Flatten | 1 ~ 16 | +| Floor | 1 ~ 16 | +| FloorDiv | 6 ~ 16 | +| FloorMod | 7 ~ 16 | +| FusedBatchNorm | 6 ~ 16 | +| FusedBatchNormV2 | 6 ~ 16 | +| FusedBatchNormV3 | 6 ~ 16 | +| Gather | 1 ~ 16 | +| GatherNd | 1 ~ 16 | +| GatherV2 | 1 ~ 16 | +| Greater | 1 ~ 16 | +| GreaterEqual | 7 ~ 16 | +| HardSwish | 14 ~ 16 | +| HashTableV2 | 8 ~ 16 | +| Identity | 1 ~ 16 | +| IdentityN | 1 ~ 16 | +| If | 1 ~ 16 | +| InvertPermutation | 11 ~ 16 | +| IsFinite | 10 ~ 16 | +| IsInf | 10 ~ 16 | +| IsNan | 9 ~ 16 | +| IteratorGetNext | 8 ~ 16 | +| IteratorV2 | 8 ~ 16 | +| LRN | 1 ~ 16 | +| LSTMBlockCell | 1 ~ 16 | +| LeakyRelu | 1 ~ 16 | +| LeftShift | 11 ~ 16 | +| Less | 1 ~ 16 | +| LessEqual | 7 ~ 16 | +| Log | 1 ~ 16 | +| LogSoftmax | 1 ~ 16 | +| LogicalAnd | 1 ~ 16 | +| LogicalNot | 1 ~ 16 | +| LogicalOr | 1 ~ 16 | +| LookupTableFindV2 | 8 ~ 16 | +| LookupTableSizeV2 | 1 ~ 16 | +| Loop | 7 ~ 16 | +| MatMul | 1 ~ 16 | +| MatrixBandPart | 7 ~ 16 | +| MatrixDeterminant | 11 ~ 16 | +| MatrixDiag | 12 ~ 16 | +| MatrixDiagPart | 11 ~ 16 | +| MatrixDiagPartV2 | 11 ~ 16 | +| MatrixDiagPartV3 | 11 ~ 16 | +| MatrixDiagV2 | 12 ~ 16 | +| MatrixDiagV3 | 12 ~ 16 | +| MatrixSetDiagV3 | 12 ~ 16 | +| Max | 1 ~ 16 | +| MaxPool | 1 ~ 16 | +| MaxPool3D | 1 ~ 16 | +| MaxPoolV2 | 1 ~ 16 | +| MaxPoolWithArgmax | 8 ~ 16 | +| Maximum | 1 ~ 16 | +| Mean | 1 ~ 16 | +| Min | 1 ~ 16 | +| Minimum | 1 ~ 16 | +| MirrorPad | 1 ~ 16 | +| Mul | 1 ~ 16 | +| Multinomial | 7 ~ 16 | +| Neg | 1 ~ 16 | +| NoOp | 1 ~ 16 | +| NonMaxSuppressionV2 | 10 ~ 16 | +| NonMaxSuppressionV3 | 10 ~ 16 | +| NonMaxSuppressionV4 | 10 ~ 16 | +| NonMaxSuppressionV5 | 10 ~ 16 | +| NotEqual | 1 ~ 16 | +| OneHot | 1 ~ 16 | +| Pack | 1 ~ 16 | +| Pad | 1 ~ 16 | +| PadV2 | 1 ~ 16 | +| ParallelDynamicStitch | 10 ~ 16 | +| Placeholder | 1 ~ 16 | +| PlaceholderV2 | 1 ~ 16 | +| PlaceholderWithDefault | 1 ~ 16 | +| Pow | 1 ~ 16 | +| Prelu | 1 ~ 16 | +| Prod | 1 ~ 16 | +| QueueDequeueManyV2 | 8 ~ 16 | +| QueueDequeueUpToV2 | 8 ~ 16 | +| QueueDequeueV2 | 8 ~ 16 | +| RFFT | 1 ~ 16 | +| RFFT2D | 1 ~ 16 | +| RaggedGather | 11 ~ 16 | +| RaggedRange | 11 ~ 16 | +| RaggedTensorFromVariant | 13 ~ 16 | +| RaggedTensorToSparse | 11 ~ 16 | +| RaggedTensorToTensor | 11 ~ 16 | +| RaggedTensorToVariant | 13 ~ 16 | +| RandomNormal | 1 ~ 16 | +| RandomNormalLike | 1 ~ 16 | +| RandomShuffle | 10 ~ 16 | +| RandomStandardNormal | 1 ~ 16 | +| RandomUniform | 1 ~ 16 | +| RandomUniformInt | 1 ~ 16 | +| RandomUniformLike | 1 ~ 16 | +| Range | 7 ~ 16 | +| RealDiv | 1 ~ 16 | +| Reciprocal | 1 ~ 16 | +| Relu | 1 ~ 16 | +| Relu6 | 1 ~ 16 | +| Reshape | 1 ~ 16 | +| ResizeBicubic | 7 ~ 16 | +| ResizeBilinear | 7 ~ 16 | +| ResizeNearestNeighbor | 7 ~ 16 | +| ReverseSequence | 8 ~ 16 (Except 9) | +| ReverseV2 | 10 ~ 16 | +| RightShift | 11 ~ 16 | +| Rint | 11 ~ 16 | +| Roll | 10 ~ 16 | +| Round | 1 ~ 16 | +| Rsqrt | 1 ~ 16 | +| SampleDistortedBoundingBox | 9 ~ 16 | +| SampleDistortedBoundingBoxV2 | 9 ~ 16 | +| Scan | 7 ~ 16 | +| ScatterNd | 11 ~ 16 | +| SegmentMax | 11 ~ 16 | +| SegmentMean | 11 ~ 16 | +| SegmentMin | 11 ~ 16 | +| SegmentProd | 11 ~ 16 | +| SegmentSum | 11 ~ 16 | +| Select | 7 ~ 16 | +| SelectV2 | 7 ~ 16 | +| Selu | 1 ~ 16 | +| Shape | 1 ~ 16 | +| Sigmoid | 1 ~ 16 | +| Sign | 1 ~ 16 | +| Sin | 7 ~ 16 | +| Sinh | 9 ~ 16 | +| Size | 1 ~ 16 | +| Slice | 1 ~ 16 | +| Softmax | 1 ~ 16 | +| SoftmaxCrossEntropyWithLogits | 7 ~ 16 | +| Softplus | 1 ~ 16 | +| Softsign | 1 ~ 16 | +| SpaceToBatchND | 1 ~ 16 | +| SpaceToDepth | 1 ~ 16 | +| SparseFillEmptyRows | 11 ~ 16 | +| SparseReshape | 11 ~ 16 | +| SparseSegmentMean | 11 ~ 16 | +| SparseSegmentMeanWithNumSegments | 11 ~ 16 | +| SparseSegmentSqrtN | 11 ~ 16 | +| SparseSegmentSqrtNWithNumSegments | 11 ~ 16 | +| SparseSegmentSum | 11 ~ 16 | +| SparseSegmentSumWithNumSegments | 11 ~ 16 | +| SparseSoftmaxCrossEntropyWithLogits | 7 ~ 16 | +| SparseToDense | 11 ~ 16 | +| Split | 1 ~ 16 | +| SplitV | 1 ~ 16 | +| Sqrt | 1 ~ 16 | +| Square | 1 ~ 16 | +| SquaredDifference | 1 ~ 16 | +| SquaredDistance | 12 ~ 16 | +| Squeeze | 1 ~ 16 | +| StatelessIf | 1 ~ 16 | +| StatelessWhile | 7 ~ 16 | +| StopGradient | 1 ~ 16 | +| StridedSlice | 1 ~ 16 | +| StringLower | 10 ~ 16 | +| StringToNumber | 9 ~ 16 | +| StringUpper | 10 ~ 16 | +| Sub | 1 ~ 16 | +| Sum | 1 ~ 16 | +| TFL_CONCATENATION | 1 ~ 16 | +| TFL_DEQUANTIZE | 1 ~ 16 | +| TFL_PRELU | 7 ~ 16 | +| TFL_QUANTIZE | 1 ~ 16 | +| TFL_TFLite_Detection_PostProcess | 11 ~ 16 | +| TFL_WHILE | 7 ~ 16 | +| Tan | 7 ~ 16 | +| Tanh | 1 ~ 16 | +| TensorListFromTensor | 7 ~ 16 | +| TensorListGetItem | 7 ~ 16 | +| TensorListLength | 7 ~ 16 | +| TensorListReserve | 7 ~ 16 | +| TensorListResize | 7 ~ 16 | +| TensorListSetItem | 7 ~ 16 | +| TensorListStack | 7 ~ 16 | +| TensorScatterAdd | 16 | +| TensorScatterUpdate | 11 ~ 16 | +| Tile | 1 ~ 16 | +| TopKV2 | 1 ~ 16 | +| Transpose | 1 ~ 16 | +| TruncateDiv | 1 ~ 16 | +| Unique | 11 ~ 16 | +| Unpack | 1 ~ 16 | +| UnsortedSegmentMax | 11 ~ 16 | +| UnsortedSegmentMin | 11 ~ 16 | +| UnsortedSegmentProd | 11 ~ 16 | +| UnsortedSegmentSum | 11 ~ 16 | +| Where | 9 ~ 16 | +| While | 7 ~ 16 | +| ZerosLike | 1 ~ 16 | ### Domain: "com.google.tensorflow" | Tensorflow Op | Convertible to ONNX Op Versions | | ------------- | ------------------------------- | diff --git a/tools/gen_doc.py b/tools/gen_doc.py index 8f86a87b5..50b6f6708 100644 --- a/tools/gen_doc.py +++ b/tools/gen_doc.py @@ -20,7 +20,7 @@ LATEST_OPSET = { - "": 15, # default domain + "": 16, # default domain "com.microsoft": 1, # microsoft domain "ai.onnx.contrib": 1, # contrib ops } From e9b6cb4fca61c87fcdaf4e9610535910c01ea4f9 Mon Sep 17 00:00:00 2001 From: Jay Zhang <36183870+fatcat-z@users.noreply.github.com> Date: Tue, 31 May 2022 16:13:01 +0800 Subject: [PATCH 14/40] Remove unuseful sync workflow. (#1955) Signed-off-by: Jay Zhang --- .github/workflows/ado-sync-issue.yml | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 .github/workflows/ado-sync-issue.yml diff --git a/.github/workflows/ado-sync-issue.yml b/.github/workflows/ado-sync-issue.yml deleted file mode 100644 index 7cf0c9049..000000000 --- a/.github/workflows/ado-sync-issue.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Sync issue to Azure DevOps work item - -on: - issues: - types: - [opened, edited, deleted, closed, reopened, labeled, unlabeled, assigned] - -jobs: - alert: - runs-on: ubuntu-latest - steps: - - uses: onnx/tensorflow-onnx@main - env: - ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}" - ado_organization: "msdata" - ado_project: "Vienna" - ado_area_path: "Vienna\\ONNX Runtime\\Shared Core\\Converters\\TensorFlow" - ado_iteration_path: "Vienna\\Backlog" - ado_wit: "Product Backlog Item" - ado_new_state: "New" - ado_active_state: "Committed" - ado_close_state: "Done" - github_token: "${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}" - log_level: 100 From a8f78ac7903493dee579304b7b1717aa9ec9706f Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Mon, 6 Jun 2022 22:11:19 +0800 Subject: [PATCH 15/40] increment main to 1.11 version (#1958) increment main to 1.11 version Signed-off-by: Deyu Huang --- VERSION_NUMBER | 2 +- tf2onnx/version.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION_NUMBER b/VERSION_NUMBER index 81c871de4..1cac385c6 100644 --- a/VERSION_NUMBER +++ b/VERSION_NUMBER @@ -1 +1 @@ -1.10.0 +1.11.0 diff --git a/tf2onnx/version.py b/tf2onnx/version.py index f799c9af3..a1f101e25 100644 --- a/tf2onnx/version.py +++ b/tf2onnx/version.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -version = '1.10.0' -git_version = '0065af6273eac0e911a0e75eab1cdf6be1c9ac7b' +version = '1.11.0' +git_version = 'e9b6cb4fca61c87fcdaf4e9610535910c01ea4f9' From 9cea9070f32ef49cfc87e88818a46d84e1bb6459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bernl=C3=B6hr?= Date: Sat, 11 Jun 2022 12:10:32 +0200 Subject: [PATCH 16/40] Transpose optimization for Softmax and LogSoftmax (fixes #1716) (#1964) * Transpose optimization for Softmax and LogSoftmax (fixes #1716) In opsets 13 and higher, the axis of the operation is arbitrary and can simply be changed according to the permutation of the Transpose. In lower opsets, Softmax always coerces its inputs to a 2D tensor, making Transpose operations necessary if the permutation moves axes between the coerced batch and feature dimensions. Signed-off-by: janbernloehr Co-authored-by: fthielke --- tests/test_optimizers.py | 124 +++++++++++++++++++++++ tf2onnx/optimizer/transpose_optimizer.py | 24 +++++ 2 files changed, 148 insertions(+) diff --git a/tests/test_optimizers.py b/tests/test_optimizers.py index 7ab159d0e..180913640 100644 --- a/tests/test_optimizers.py +++ b/tests/test_optimizers.py @@ -1369,6 +1369,130 @@ def test_transpose_argmax(self): self.run_transpose_compare(["res"], {"X": np.random.randn(*input_shape).astype(np.float32)}, model_proto, remaining_transpose_num=0) + @check_opset_max_version( + 12, "Before opset 13, Softmax coerced its inputs to 2D and can thus only be optimized for certain permutations" + ) + def test_transpose_softmax_valid_perm(self): + input_shape = [4, 4, 4, 4] + node0 = helper.make_node("Transpose", ["X"], ["Y"], perm=[0, 2, 3, 1], name="trans_1") + node1 = helper.make_node("Softmax", ["Y"], ["Z"], axis=1, name="softmax") + node2 = helper.make_node("Transpose", ["Z"], ["res"], perm=[0, 3, 1, 2], name="trans_2") + + graph = helper.make_graph( + [node0, node1, node2], + "transpose-softmax-test", + [helper.make_tensor_value_info("X", TensorProto.FLOAT, input_shape)], + [helper.make_tensor_value_info("res", TensorProto.FLOAT, input_shape)], + ) + + model_proto = self.make_model(graph, producer_name="onnx-tests") + self.run_transpose_compare( + ["res"], {"X": np.random.randn(*input_shape).astype(np.float32)}, model_proto, remaining_transpose_num=0 + ) + + @check_opset_max_version( + 12, "Before opset 13, Softmax coerced its inputs to 2D and can thus only be optimized for certain permutations" + ) + def test_transpose_softmax_invalid_perm(self): + input_shape = [4, 4, 4, 4] + node0 = helper.make_node("Transpose", ["X"], ["Y"], perm=[0, 2, 3, 1], name="trans_1") + node1 = helper.make_node("Softmax", ["Y"], ["Z"], axis=3, name="softmax") + node2 = helper.make_node("Transpose", ["Z"], ["res"], perm=[0, 3, 1, 2], name="trans_2") + + graph = helper.make_graph( + [node0, node1, node2], + "transpose-softmax-test", + [helper.make_tensor_value_info("X", TensorProto.FLOAT, input_shape)], + [helper.make_tensor_value_info("res", TensorProto.FLOAT, input_shape)], + ) + + model_proto = self.make_model(graph, producer_name="onnx-tests") + self.run_transpose_compare( + ["res"], {"X": np.random.randn(*input_shape).astype(np.float32)}, model_proto, remaining_transpose_num=2 + ) + + @check_opset_min_version(13, "Softmax can be optimized for all permutations since opset 13") + def test_transpose_softmax_13(self): + input_shape = [4, 4, 4, 4] + node0 = helper.make_node("Transpose", ["X"], ["Y"], perm=[0, 2, 3, 1], name="trans_1") + node1 = helper.make_node("Softmax", ["Y"], ["Z"], axis=3, name="softmax") + node2 = helper.make_node("Transpose", ["Z"], ["res"], perm=[0, 3, 1, 2], name="trans_2") + + graph = helper.make_graph( + [node0, node1, node2], + "transpose-softmax-test", + [helper.make_tensor_value_info("X", TensorProto.FLOAT, input_shape)], + [helper.make_tensor_value_info("res", TensorProto.FLOAT, input_shape)], + ) + + model_proto = self.make_model(graph, producer_name="onnx-tests") + self.run_transpose_compare( + ["res"], {"X": np.random.randn(*input_shape).astype(np.float32)}, model_proto, remaining_transpose_num=0 + ) + + @check_opset_max_version( + 12, + "Before opset 13, LogSoftmax coerced its inputs to 2D and can thus only be optimized for certain permutations", + ) + def test_transpose_logsoftmax_valid_perm(self): + input_shape = [4, 4, 4, 4] + node0 = helper.make_node("Transpose", ["X"], ["Y"], perm=[0, 2, 3, 1], name="trans_1") + node1 = helper.make_node("LogSoftmax", ["Y"], ["Z"], axis=1, name="logsoftmax") + node2 = helper.make_node("Transpose", ["Z"], ["res"], perm=[0, 3, 1, 2], name="trans_2") + + graph = helper.make_graph( + [node0, node1, node2], + "transpose-logsoftmax-test", + [helper.make_tensor_value_info("X", TensorProto.FLOAT, input_shape)], + [helper.make_tensor_value_info("res", TensorProto.FLOAT, input_shape)], + ) + + model_proto = self.make_model(graph, producer_name="onnx-tests") + self.run_transpose_compare( + ["res"], {"X": np.random.randn(*input_shape).astype(np.float32)}, model_proto, remaining_transpose_num=0 + ) + + @check_opset_max_version( + 12, + "Before opset 13, LogSoftmax coerced its inputs to 2D and can thus only be optimized for certain permutations", + ) + def test_transpose_logsoftmax_invalid_perm(self): + input_shape = [4, 4, 4, 4] + node0 = helper.make_node("Transpose", ["X"], ["Y"], perm=[0, 2, 3, 1], name="trans_1") + node1 = helper.make_node("LogSoftmax", ["Y"], ["Z"], axis=3, name="logsoftmax") + node2 = helper.make_node("Transpose", ["Z"], ["res"], perm=[0, 3, 1, 2], name="trans_2") + + graph = helper.make_graph( + [node0, node1, node2], + "transpose-logsoftmax-test", + [helper.make_tensor_value_info("X", TensorProto.FLOAT, input_shape)], + [helper.make_tensor_value_info("res", TensorProto.FLOAT, input_shape)], + ) + + model_proto = self.make_model(graph, producer_name="onnx-tests") + self.run_transpose_compare( + ["res"], {"X": np.random.randn(*input_shape).astype(np.float32)}, model_proto, remaining_transpose_num=2 + ) + + @check_opset_min_version(13, "LogSoftmax can be optimized for all permutations since opset 13") + def test_transpose_logsoftmax_13(self): + input_shape = [4, 4, 4, 4] + node0 = helper.make_node("Transpose", ["X"], ["Y"], perm=[0, 2, 3, 1], name="trans_1") + node1 = helper.make_node("LogSoftmax", ["Y"], ["Z"], axis=3, name="logsoftmax") + node2 = helper.make_node("Transpose", ["Z"], ["res"], perm=[0, 3, 1, 2], name="trans_2") + + graph = helper.make_graph( + [node0, node1, node2], + "transpose-logsoftmax-test", + [helper.make_tensor_value_info("X", TensorProto.FLOAT, input_shape)], + [helper.make_tensor_value_info("res", TensorProto.FLOAT, input_shape)], + ) + + model_proto = self.make_model(graph, producer_name="onnx-tests") + self.run_transpose_compare( + ["res"], {"X": np.random.randn(*input_shape).astype(np.float32)}, model_proto, remaining_transpose_num=0 + ) + def test_transpose_tile(self): input_shape = [1, 2, 3, 4] diff --git a/tf2onnx/optimizer/transpose_optimizer.py b/tf2onnx/optimizer/transpose_optimizer.py index 0c55d039e..dd5377f0a 100644 --- a/tf2onnx/optimizer/transpose_optimizer.py +++ b/tf2onnx/optimizer/transpose_optimizer.py @@ -205,6 +205,7 @@ def _initialize_handlers(self): "Identity": self._identity_handler, "LeakyRelu": self._simple_through_handler, "Log": self._simple_through_handler, + "LogSoftmax": self._softmax_handler, "Max": self._maxmin_handler, "Min": self._maxmin_handler, "Mul": self._mul_handler, @@ -223,6 +224,7 @@ def _initialize_handlers(self): "Relu": self._simple_through_handler, "Shape": self._shape_handler, "Sigmoid": self._simple_through_handler, + "Softmax": self._softmax_handler, "Sum": self._sum_handler, "Slice": self._slice_handler, "Split": self._split_handler, @@ -827,6 +829,28 @@ def permute_pads(pads): def _prelu_handler(self, trans, node): return self._handle_node_having_branches(trans, node) + def _softmax_handler(self, trans, node): + trans_rank = get_transpose_rank(trans) + perm = trans.get_attr("perm").ints + + if self._g.opset >= 13: + # Softmax operates on an arbitrary axis since opset 13 + axis = node.get_attr_value("axis", -1) + new_axis = perm[axis + trans_rank if axis < 0 else axis] + if not self._switch_transpose_and_node(node, trans): + return False + node.set_attr("axis", new_axis) + return True + + # For older opsets, the "axis" attribute determines the coercion point for coercing the input tensor to 2D. + # We can safely switch transpose and node if the permutation does not make any axes cross that boundary. + coercion_axis = node.get_attr_value("axis", 1) + for from_axis, to_axis in enumerate(perm): + if (from_axis < coercion_axis <= to_axis) or (from_axis >= coercion_axis > to_axis): + return False + + return self._switch_transpose_and_node(node, trans) + def _arg_min_max_handler(self, trans, node): axis = node.get_attr_value("axis", 0) node.set_attr("axes", [axis]) From 89c4c5c9edc661b10621b44705f720edb9c519ef Mon Sep 17 00:00:00 2001 From: Jay Zhang <36183870+fatcat-z@users.noreply.github.com> Date: Wed, 15 Jun 2022 15:29:18 +0800 Subject: [PATCH 17/40] The from_tflite() function should accept None as default value of input_names and output_names. (#1967) * The from_tflite() function should not change the value of None to an empty list for input_names and output_names. * Change the way to validate a list is None or Emtpy. Signed-off-by: Jay Zhang --- tests/test_api.py | 24 ++++++++++++++++++++---- tf2onnx/convert.py | 4 ---- tf2onnx/tflite_utils.py | 4 ++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index 79f8780f4..2b25b1b64 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -237,11 +237,27 @@ def test_tflite(self): x_val = np.array([1.0, 2.0, -3.0, -4.0], dtype=np.float32).reshape((2, 2)) model_proto, _ = tf2onnx.convert.from_tflite("tests/models/regression/tflite/test_api_model.tflite", - input_names=['input'], output_names=['output'], + input_names=["input"], output_names=["output"], output_path=output_path) - output_names = [n.name for n in model_proto.graph.output] - oy = self.run_onnxruntime(output_path, {"input": x_val}, output_names) - self.assertTrue(output_names[0] == "output") + actual_output_names = [n.name for n in model_proto.graph.output] + oy = self.run_onnxruntime(output_path, {"input": x_val}, actual_output_names) + + self.assertTrue(actual_output_names[0] == "output") + exp_result = tf.add(x_val, x_val) + self.assertAllClose(exp_result, oy[0], rtol=0.1, atol=0.1) + + @check_tf_min_version("2.0") + def test_tflite_without_input_output_names(self): + output_path = os.path.join(self.test_data_directory, "model.onnx") + + x_val = np.array([1.0, 2.0, -3.0, -4.0], dtype=np.float32).reshape((2, 2)) + model_proto, _ = tf2onnx.convert.from_tflite("tests/models/regression/tflite/test_api_model.tflite", + output_path=output_path) + actual_input_names = [n.name for n in model_proto.graph.input] + actual_output_names = [n.name for n in model_proto.graph.output] + oy = self.run_onnxruntime(output_path, {actual_input_names[0]: x_val}, output_names=None) + + self.assertTrue(actual_output_names[0] == "output") exp_result = tf.add(x_val, x_val) self.assertAllClose(exp_result, oy[0], rtol=0.1, atol=0.1) diff --git a/tf2onnx/convert.py b/tf2onnx/convert.py index 64a3db36c..0a1069496 100644 --- a/tf2onnx/convert.py +++ b/tf2onnx/convert.py @@ -663,10 +663,6 @@ def from_tflite(tflite_path, input_names=None, output_names=None, opset=None, cu """ if not tflite_path: raise ValueError("tflite_path needs to be provided") - if not input_names: - input_names = [] - if not output_names: - output_names = [] with tf.device("/cpu:0"): model_proto, external_tensor_storage = _convert_common( diff --git a/tf2onnx/tflite_utils.py b/tf2onnx/tflite_utils.py index b5974c850..111f162f4 100644 --- a/tf2onnx/tflite_utils.py +++ b/tf2onnx/tflite_utils.py @@ -156,9 +156,9 @@ def graphs_from_tflite(tflite_path, input_names=None, output_names=None): if is_main_g: # Override IO in main graph utils.check_io(input_names, output_names, output_shapes.keys()) - if input_names is not None: + if input_names: g_inputs = input_names - if output_names is not None: + if output_names: g_outputs = output_names g = Graph(onnx_nodes, output_shapes, dtypes, input_names=g_inputs, output_names=g_outputs, is_subgraph=not is_main_g, graph_name=graph_name) From b027bb2e5c9316d3949453987c81fea90e416ac8 Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Fri, 17 Jun 2022 09:43:18 +0800 Subject: [PATCH 18/40] Change Loop op with maximum iterations input M equals to empty string (#1971) * make Loop op with maximum iterations M equal to empty string to match onnx spec Signed-off-by: Deyu Huang --- tf2onnx/onnx_opset/controlflow.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/tf2onnx/onnx_opset/controlflow.py b/tf2onnx/onnx_opset/controlflow.py index b6dd5a14b..b244bd3f1 100644 --- a/tf2onnx/onnx_opset/controlflow.py +++ b/tf2onnx/onnx_opset/controlflow.py @@ -381,29 +381,32 @@ def version_7(cls, ctx, node, **kwargs): # may be removed from output_names below output_names = node.output.copy() - # Make maximum_iterations int64 and replace -1(tf) with maxsize(onnx). If the const node has no other + # Make maximum_iterations int64. If the const node has no other # consumers, modify it in place. Otherwise, make a new const node and leave the original unchanged. # if maximum_iterations is not const,should add an cast node(cast to int64) maximum_iterations_name = node.input[1] if node.inputs[1].is_const(): maximum_iterations = node.inputs[1].get_tensor_value() - if maximum_iterations == -1: - maximum_iterations = np.iinfo(np.int64).max - consumers = ctx.find_output_consumers(maximum_iterations_name) - external_consumers = [c for c in consumers if c != node and c.type != 'TensorListReserve'] - if len(external_consumers) == 0: - ctx.remove_node(node.inputs[1].name) + # maximum_iterations with -1(tf) means it doesn't set the maximum count. + # For onnx Loop op optional input `M`(int64), represents a maximum trip-count. Set empty string to skip. + if maximum_iterations != -1: + consumers = ctx.find_output_consumers(maximum_iterations_name) + external_consumers = [c for c in consumers if c != node and c.type != 'TensorListReserve'] + if len(external_consumers) == 0: + ctx.remove_node(node.inputs[1].name) + else: + maximum_iterations_name = utils.make_name(node.inputs[1].name) + ctx.make_const(maximum_iterations_name, np.array(maximum_iterations, dtype=np.int64)) + ctx.replace_input(node, node.input[1], maximum_iterations_name, 1) + maximum_iterations_m = maximum_iterations_name else: - maximum_iterations_name = utils.make_name(node.inputs[1].name) - ctx.make_const(maximum_iterations_name, np.array(maximum_iterations, dtype=np.int64)) - ctx.replace_input(node, node.input[1], maximum_iterations_name, 1) - maximum_iterations_int64 = maximum_iterations_name + maximum_iterations_m = "" else: cast_inputs = [maximum_iterations_name] attr = {"to": onnx_pb.TensorProto.INT64} cast_name = node.name + "_cast" cast_node = ctx.make_node("Cast", cast_inputs, attr, name=cast_name) - maximum_iterations_int64 = cast_node.output[0] + maximum_iterations_m = cast_node.output[0] cond_name = node.get_attr_str("cond") cond_graph = find_function(cond_name) @@ -427,7 +430,7 @@ def version_7(cls, ctx, node, **kwargs): cond_input_to_state_var[cond_graph.input_names[idx]] = maximum_iterations_name continue if idx < 2: - # skip [0,1] loop_counter, max_iterations + # skip [0,1] loop_counter, max_iterations continue n = node.inputs[idx] if n.type in ["TensorListReserve", "TensorListResize"]: @@ -511,7 +514,7 @@ def version_7(cls, ctx, node, **kwargs): output_names = output_names[2:] branches = {"body": body} - loop_node = ctx.make_node("Loop", [maximum_iterations_int64, cond_outputs[0]] + loop_vars, + loop_node = ctx.make_node("Loop", [maximum_iterations_m, cond_outputs[0]] + loop_vars, output_count=len(output_shapes), name=node.name + "_loop", shapes=output_shapes, dtypes=output_dtypes, skip_conversion=True, branches=branches) From 1d7629794d9095e224679db4cb12bafc7e02cefb Mon Sep 17 00:00:00 2001 From: andife Date: Wed, 22 Jun 2022 05:33:30 +0200 Subject: [PATCH 19/40] Update README.md file (#1976) Update README.md typos Signed-off-by: andife --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 806573a41..ad640e21e 100644 --- a/README.md +++ b/README.md @@ -268,7 +268,7 @@ optional arguments: ``` ```run_pretrained_models.py``` will run the TensorFlow model, captures the TensorFlow output and runs the same test against the specified ONNX backend after converting the model. -If the option ```--perf csv-file``` is specified, we'll capture the timeing for inferece of tensorflow and onnx runtime and write the result into the given csv file. +If the option ```--perf csv-file``` is specified, we'll capture the timing for inference of tensorflow and onnx runtime and write the result into the given csv file. You call it for example with: ``` From 7a57a6b5c63fc8fdcba4a650defbef50419998ef Mon Sep 17 00:00:00 2001 From: andife Date: Fri, 24 Jun 2022 10:32:33 +0200 Subject: [PATCH 20/40] Update CONTRIBUTING.md CLA to DCO (#1978) Update CLA -> DCO (as all onnx projects) Signed-off-by: andife --- CONTRIBUTING.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c7d863e1b..d30338f50 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,14 +24,19 @@ New code *must* be accompanied by unit tests. Please see [Coding Conventions and Standards](http://google.github.io/styleguide/pyguide.html) # Licensing guidelines -This project welcomes contributions and suggestions. Most contributions require you to -agree to a Contributor License Agreement (CLA) declaring that you have the right to, -and actually do, grant us the rights to use your contribution. For details, visit -https://cla-assistant.io/onnx/tensorflow-onnx. - -When you submit a pull request, a CLA-bot will automatically determine whether you need -to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the -instructions provided by the bot. You will only need to do this once across all repositories using our CLA. +This project welcomes contributions and suggestions. The contributions require you to +agree the Developer Certificate of Origin (DCO) declaring that you have the right to, +and actually do, grant us the rights to use your contribution. + +When you submit a pull request, a DCO-bot will automatically determine whether you need +to provide a DCO and decorate the PR appropriately. + +You are ready to sign your code by using the `-s` flag during your commits. + +```sh +git commit -s +``` + # Code of conduct This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). From 3cf62e23021dda6ffd13ac1e6fe7742fe6137987 Mon Sep 17 00:00:00 2001 From: Jay Zhang <36183870+fatcat-z@users.noreply.github.com> Date: Mon, 27 Jun 2022 01:10:29 +0800 Subject: [PATCH 21/40] Update Keras related tests to support latest TF version. (#1980) * Update all Keras related tests to support latest TF version. Signed-off-by: Jay Zhang --- .../keras2onnx_application_tests.yml | 49 +++++++------------ .../azure_pipelines/keras2onnx_unit_test.yml | 34 ++++++------- .../templates/keras2onnx_unit_test.yml | 2 +- .../trimmed_keras2onnx_application_tests.yml | 12 ++--- .../trimmed_keras2onnx_unit_test.yml | 28 ++++++++--- .../mock_keras2onnx/proto/__init__.py | 24 ++++++--- tests/keras2onnx_unit_tests/test_layers.py | 23 +++++---- 7 files changed, 92 insertions(+), 80 deletions(-) diff --git a/ci_build/azure_pipelines/keras2onnx_application_tests.yml b/ci_build/azure_pipelines/keras2onnx_application_tests.yml index 47d55f2b4..b7184e9f1 100644 --- a/ci_build/azure_pipelines/keras2onnx_application_tests.yml +++ b/ci_build/azure_pipelines/keras2onnx_application_tests.yml @@ -8,18 +8,6 @@ jobs: vmImage: 'ubuntu-latest' strategy: matrix: - Python37-onnx1.5: - python.version: '3.7' - ONNX_PATH: onnx==1.5.0 - INSTALL_KERAS: pip install keras==2.2.4 - UNINSTALL_KERAS: - INSTALL_TENSORFLOW: pip install tensorflow==1.15.0 - INSTALL_ORT: pip install onnxruntime==1.8.0 - INSTALL_KERAS_RESNET: pip install keras-resnet - INSTALL_TRANSFORMERS: - INSTALL_NUMPY: - NIGHTLY_BUILD_TEST: python run_all.py --exclude "test_keras_applications_v2.py" - Python37-onnx1.6: python.version: '3.7' ONNX_PATH: onnx==1.6.0 @@ -44,7 +32,7 @@ jobs: INSTALL_NUMPY: NIGHTLY_BUILD_TEST: python run_all.py --exclude "test_keras_applications_v2.py" - Python37-onnx1.11: + Python37-onnx1.11-tf1.15: python.version: '3.7' ONNX_PATH: onnx==1.11.0 INSTALL_KERAS: pip install keras==2.3.1 @@ -56,18 +44,30 @@ jobs: INSTALL_NUMPY: pip install numpy==1.19.0 NIGHTLY_BUILD_TEST: python run_all_v2.py --exclude "test_keras_applications_v2.py" - Python38-tf2.x: - python.version: '3.8' + Python37-onnx1.11-tf2.5: + python.version: '3.7' ONNX_PATH: onnx==1.11.0 - INSTALL_KERAS: + INSTALL_KERAS: UNINSTALL_KERAS: pip uninstall keras -y INSTALL_TENSORFLOW: pip install tensorflow==2.5.0 - INSTALL_ORT: pip install onnxruntime==1.9.0 + INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_KERAS_RESNET: pip install keras-resnet INSTALL_TRANSFORMERS: pip install transformers==3.4.0 INSTALL_NUMPY: pip install numpy==1.19.0 NIGHTLY_BUILD_TEST: python run_all_v2.py + Python38-onnx1.11-tf2.8: + python.version: '3.7' + ONNX_PATH: onnx==1.11.0 + INSTALL_KERAS: + UNINSTALL_KERAS: + INSTALL_TENSORFLOW: pip install tensorflow==2.8.0 + INSTALL_ORT: pip install onnxruntime==1.11.0 + INSTALL_KERAS_RESNET: pip install keras-resnet + INSTALL_TRANSFORMERS: pip install transformers==3.4.0 + INSTALL_NUMPY: + NIGHTLY_BUILD_TEST: python run_all_v2.py + steps: - template: 'templates/keras2onnx_application_tests.yml' parameters: @@ -79,17 +79,6 @@ jobs: vmImage: 'windows-2019' strategy: matrix: - Python37-onnx1.5: - python.version: '3.7' - ONNX_PATH: onnx==1.5.0 - INSTALL_KERAS: pip install keras==2.2.4 - UNINSTALL_KERAS: - INSTALL_TENSORFLOW: pip install tensorflow==1.15.0 - INSTALL_ORT: pip install onnxruntime==1.8.0 - INSTALL_KERAS_RESNET: pip install keras-resnet - INSTALL_TRANSFORMERS: - NIGHTLY_BUILD_TEST: python run_all_v2.py --exclude "test_keras_applications_v2.py" - Python37-onnx1.6: python.version: '3.7' ONNX_PATH: onnx==1.6.0 @@ -114,7 +103,7 @@ jobs: INSTALL_NUMPY: pip install numpy==1.19.0 NIGHTLY_BUILD_TEST: python run_all_v2.py --exclude "test_keras_applications_v2.py" - Python37-onnx1.11: + Python37-onnx1.11-tf1.15: python.version: '3.7' ONNX_PATH: onnx==1.11.0 INSTALL_KERAS: pip install keras==2.3.1 @@ -129,7 +118,7 @@ jobs: Python38-tf2.x: python.version: '3.8' ONNX_PATH: onnx==1.11.0 - INSTALL_KERAS: + INSTALL_KERAS: UNINSTALL_KERAS: pip uninstall keras -y INSTALL_TENSORFLOW: pip install tensorflow==2.5.0 INSTALL_ORT: pip install onnxruntime==1.9.0 diff --git a/ci_build/azure_pipelines/keras2onnx_unit_test.yml b/ci_build/azure_pipelines/keras2onnx_unit_test.yml index a812543f2..f1997b6a7 100644 --- a/ci_build/azure_pipelines/keras2onnx_unit_test.yml +++ b/ci_build/azure_pipelines/keras2onnx_unit_test.yml @@ -16,13 +16,6 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.9.0 INSTALL_NUMPY: pip install numpy==1.19.0 - Python37-tf2.1: - python.version: '3.7' - ONNX_PATH: onnx==1.11.0 - TENSORFLOW_PATH: tensorflow-cpu==2.1.0 - INSTALL_ORT: pip install onnxruntime==1.11.0 - INSTALL_NUMPY: pip install numpy==1.19.0 - Python38-tf2.2: python.version: '3.8' ONNX_PATH: onnx==1.11.0 @@ -44,14 +37,14 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 - ############ Pure Keras Unit Tests ############ - Keras-Py37-tf1.15.0: - python.version: '3.7' - ONNX_PATH: onnx==1.10.2 - KERAS: keras==2.2.5 - TENSORFLOW_PATH: tensorflow==1.15.0 - INSTALL_ORT: pip install onnxruntime==1.9.0 + Python38-tf2.8: + python.version: '3.8' + ONNX_PATH: onnx==1.11.0 + TENSORFLOW_PATH: tensorflow-cpu==2.8.0 + INSTALL_ORT: pip install onnxruntime==1.11.0 + INSTALL_NUMPY: + ############ Pure Keras Unit Tests ############ Keras-Py37-tf1.15.0: python.version: '3.7' ONNX_PATH: onnx==1.11.0 @@ -93,12 +86,6 @@ jobs: ONNX_PATH: onnx==1.10.2 TENSORFLOW_PATH: tensorflow==1.15.0 INSTALL_ORT: pip install onnxruntime==1.9.0 - - Python37-tf2.1: - python.version: '3.7' - ONNX_PATH: onnx==1.11.0 - TENSORFLOW_PATH: tensorflow-cpu==2.1.0 - INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 Python37-tf2.2: @@ -122,6 +109,13 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 + Python37-tf2.8: + python.version: '3.8' + ONNX_PATH: onnx==1.11.0 + TENSORFLOW_PATH: tensorflow-cpu==2.8.0 + INSTALL_ORT: pip install onnxruntime==1.11.0 + INSTALL_NUMPY: + ############ Pure Keras Unit Tests ############ Keras-Py37-tf1.15.0: python.version: '3.7' diff --git a/ci_build/azure_pipelines/templates/keras2onnx_unit_test.yml b/ci_build/azure_pipelines/templates/keras2onnx_unit_test.yml index ecf40053c..00ac1d739 100644 --- a/ci_build/azure_pipelines/templates/keras2onnx_unit_test.yml +++ b/ci_build/azure_pipelines/templates/keras2onnx_unit_test.yml @@ -69,7 +69,7 @@ steps: - script: | call activate py$(python.version) - python -m pip install --upgrade pip numpy==1.19 + python -m pip install --upgrade pip numpy echo Test numpy installation... && python -c "import numpy" pip install %ONNX_PATH% pip uninstall -y protobuf diff --git a/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml b/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml index 8447a7d72..edf1c2f55 100644 --- a/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml +++ b/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml @@ -10,26 +10,26 @@ jobs: matrix: Python37-onnx1.10: python.version: '3.7' - ONNX_PATH: onnx==1.10.2 + ONNX_PATH: onnx==1.11.0 INSTALL_KERAS: pip install keras==2.2.4 UNINSTALL_KERAS: INSTALL_TENSORFLOW: pip install tensorflow==1.15.0 - INSTALL_ORT: pip install onnxruntime==1.9.0 + INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_KERAS_RESNET: pip install keras-resnet INSTALL_TRANSFORMERS: INSTALL_NUMPY: pip install numpy==1.19.0 NIGHTLY_BUILD_TEST: python run_all.py --exclude "test_keras_applications_v2.py" - Python38-tf2: + Python38-tf2.x: python.version: '3.8' ONNX_PATH: onnx==1.11.0 INSTALL_KERAS: - UNINSTALL_KERAS: pip uninstall keras -y - INSTALL_TENSORFLOW: pip install tensorflow==2.5.0 + UNINSTALL_KERAS: + INSTALL_TENSORFLOW: pip install tensorflow==2.9.0 INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_KERAS_RESNET: pip install keras-resnet INSTALL_TRANSFORMERS: pip install transformers==3.4.0 - INSTALL_NUMPY: pip install numpy==1.19.0 + INSTALL_NUMPY: NIGHTLY_BUILD_TEST: python run_all_v2.py steps: diff --git a/ci_build/azure_pipelines/trimmed_keras2onnx_unit_test.yml b/ci_build/azure_pipelines/trimmed_keras2onnx_unit_test.yml index 4ad174fa4..12d4d6e73 100644 --- a/ci_build/azure_pipelines/trimmed_keras2onnx_unit_test.yml +++ b/ci_build/azure_pipelines/trimmed_keras2onnx_unit_test.yml @@ -11,17 +11,17 @@ jobs: ############ TF Keras Unit Tests ############ Python37-tf1.15: python.version: '3.7' - ONNX_PATH: onnx==1.10.2 + ONNX_PATH: onnx==1.11.0 TENSORFLOW_PATH: tensorflow==1.15.0 - INSTALL_ORT: pip install onnxruntime==1.9.0 + INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 - Python38-tf2.5: + Python38-tf2.9: python.version: '3.8' ONNX_PATH: onnx==1.11.0 - TENSORFLOW_PATH: tensorflow-cpu==2.5.0 + TENSORFLOW_PATH: tensorflow-cpu==2.9.0 INSTALL_ORT: pip install onnxruntime==1.11.0 - INSTALL_NUMPY: pip install numpy==1.19.0 + INSTALL_NUMPY: ############ Pure Keras Unit Tests ############ Keras-Py37-tf1.15.0: @@ -41,6 +41,13 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 + Keras-Py38-tf2.9.0: + python.version: '3.8' + ONNX_PATH: -i onnx==1.11.0 + TENSORFLOW_PATH: tensorflow==2.9.0 + INSTALL_ORT: pip install onnxruntime==1.11.0 + INSTALL_NUMPY: + steps: - template: 'templates/keras2onnx_unit_test.yml' parameters: @@ -54,9 +61,9 @@ jobs: ############ TF Keras Unit Tests ############ Python37-tf-1.15: python.version: '3.7' - ONNX_PATH: onnx==1.10.2 + ONNX_PATH: onnx==1.11.0 TENSORFLOW_PATH: tensorflow==1.15.0 - INSTALL_ORT: pip install onnxruntime==1.9.0 + INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 Python37-tf2.3: @@ -66,6 +73,13 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 + Python38-tf2.9: + python.version: '3.8' + ONNX_PATH: onnx==1.11.0 + TENSORFLOW_PATH: tensorflow-cpu==2.9.0 + INSTALL_ORT: pip install onnxruntime==1.11.0 + INSTALL_NUMPY: + ############ Pure Keras Unit Tests ############ Keras-Py37-tf2.2.0: python.version: '3.7' diff --git a/tests/keras2onnx_unit_tests/mock_keras2onnx/proto/__init__.py b/tests/keras2onnx_unit_tests/mock_keras2onnx/proto/__init__.py index b70b53512..d8720fe62 100644 --- a/tests/keras2onnx_unit_tests/mock_keras2onnx/proto/__init__.py +++ b/tests/keras2onnx_unit_tests/mock_keras2onnx/proto/__init__.py @@ -2,7 +2,7 @@ import os import tensorflow -from distutils.version import StrictVersion +from packaging.version import Version # Rather than using ONNX protobuf definition throughout our codebase, we import ONNX protobuf definition here so that # we can conduct quick fixes by overwriting ONNX functions without changing any lines elsewhere. @@ -22,11 +22,15 @@ def _check_onnx_version(): def is_tensorflow_older_than(version_str): - return StrictVersion(tensorflow.__version__.split('-')[0]) < StrictVersion(version_str) + return Version(tensorflow.__version__.split('-')[0]) < Version(version_str) def is_tensorflow_later_than(version_str): - return StrictVersion(tensorflow.__version__.split('-')[0]) > StrictVersion(version_str) + return Version(tensorflow.__version__.split('-')[0]) > Version(version_str) + + +def python_keras_is_deprecated(): + return is_tensorflow_later_than("2.5.0") is_tf_keras = False @@ -38,7 +42,10 @@ def is_tensorflow_later_than(version_str): is_tf_keras = str_tk_keras != '0' if is_tf_keras: - from tensorflow.python import keras + if python_keras_is_deprecated(): + from tensorflow import keras + else: + from tensorflow.python import keras else: try: import keras @@ -47,12 +54,15 @@ def is_tensorflow_later_than(version_str): is_tf_keras = True except ImportError: is_tf_keras = True - from tensorflow.python import keras + if python_keras_is_deprecated(): + from tensorflow import keras + else: + from tensorflow.python import keras def is_keras_older_than(version_str): - return StrictVersion(keras.__version__.split('-')[0]) < StrictVersion(version_str) + return Version(keras.__version__.split('-')[0]) < Version(version_str) def is_keras_later_than(version_str): - return StrictVersion(keras.__version__.split('-')[0]) > StrictVersion(version_str) + return Version(keras.__version__.split('-')[0]) > Version(version_str) diff --git a/tests/keras2onnx_unit_tests/test_layers.py b/tests/keras2onnx_unit_tests/test_layers.py index 1e32ea5dd..7d4a32979 100644 --- a/tests/keras2onnx_unit_tests/test_layers.py +++ b/tests/keras2onnx_unit_tests/test_layers.py @@ -6,13 +6,18 @@ from mock_keras2onnx.proto.tfcompat import is_tf2, tensorflow as tf from mock_keras2onnx.proto import (keras, is_tf_keras, is_tensorflow_older_than, is_tensorflow_later_than, - is_keras_older_than, is_keras_later_than) + is_keras_older_than, is_keras_later_than, python_keras_is_deprecated) from test_utils import no_loops_in_tf2, all_recurrents_should_bidirectional K = keras.backend Activation = keras.layers.Activation Add = keras.layers.Add -advanced_activations = keras.layers.advanced_activations +if python_keras_is_deprecated(): + advanced_activations = keras.layers + layers_core = keras.layers +else: + advanced_activations = keras.layers.advanced_activations + layers_core = keras.layers.core AlphaDropout = keras.layers.AlphaDropout Average = keras.layers.Average AveragePooling1D = keras.layers.AveragePooling1D @@ -72,7 +77,7 @@ LSTM_CLASSES = [(LSTM, LSTMCell, "v1")] RNN_CLASSES = [SimpleRNN, GRU, LSTM] -if is_tf_keras and is_tensorflow_later_than("1.14.0"): +if is_tf_keras and is_tensorflow_later_than("1.14.0") and not python_keras_is_deprecated(): # Add the TF v2 compatability layers (available after TF 1.14) from tensorflow.python.keras.layers import recurrent_v2 GRU_CLASSES.append((recurrent_v2.GRU, "v2")) @@ -1259,7 +1264,7 @@ def test_conv3d_transpose(conv3trans_runner): def test_flatten(runner): model = keras.Sequential() - model.add(keras.layers.core.Flatten(input_shape=(3, 2))) + model.add(layers_core.Flatten(input_shape=(3, 2))) model.add(Dense(3)) onnx_model = convert_keras(model, model.name) @@ -1303,7 +1308,7 @@ def test_flatten2(runner): def test_reshape(runner): model = keras.Sequential() - model.add(keras.layers.core.Reshape((2, 3), input_shape=(3, 2))) + model.add(layers_core.Reshape((2, 3), input_shape=(3, 2))) onnx_model = convert_keras(model, model.name) data = np.array([[[1, 2], [3, 4], [5, 6]]]).astype(np.float32) @@ -1314,7 +1319,7 @@ def test_reshape(runner): def test_permute(runner): model = keras.Sequential() - model.add(keras.layers.core.Permute((2, 1), input_shape=(3, 2))) + model.add(layers_core.Permute((2, 1), input_shape=(3, 2))) onnx_model = convert_keras(model, model.name) data = np.array([[[1, 2], [3, 4], [5, 6]]]).astype(np.float32) @@ -1325,7 +1330,7 @@ def test_permute(runner): def test_repeat_vector(runner): model = keras.Sequential() - model.add(keras.layers.core.RepeatVector(3, input_shape=(4,))) + model.add(layers_core.RepeatVector(3, input_shape=(4,))) onnx_model = convert_keras(model, model.name) data = _asarray(1, 2, 3, 4) @@ -1596,11 +1601,11 @@ def test_crop(misc_conv_runner): misc_conv_runner(layer, ishape, opset_) for data_format_ in ['channels_last', 'channels_first']: - ishape = (20, 20, 1) + ishape = (20, 20, 10) for crop_v in [2, (2, 2), ((1, 2), (2, 3))]: layer = Cropping2D(cropping=crop_v, data_format=data_format_) misc_conv_runner(layer, ishape, opset_) - ishape = (20, 20, 20, 1) + ishape = (20, 20, 20, 10) for crop_v in [2, (2, 3, 4), ((1, 2), (2, 3), (3, 5))]: layer = Cropping3D(cropping=crop_v, data_format=data_format_) misc_conv_runner(layer, ishape, opset_) From 6905d050a3e3fffbcf4a881c13932aaed2bd93be Mon Sep 17 00:00:00 2001 From: Jay Zhang <36183870+fatcat-z@users.noreply.github.com> Date: Wed, 29 Jun 2022 17:39:35 +0800 Subject: [PATCH 22/40] Fix a test issue in keras2onnx_application_tests.yml. (#1982) * Update tests in nightly keras-application-test. Signed-off-by: Jay Zhang --- .../keras2onnx_application_tests.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ci_build/azure_pipelines/keras2onnx_application_tests.yml b/ci_build/azure_pipelines/keras2onnx_application_tests.yml index b7184e9f1..149c8cb64 100644 --- a/ci_build/azure_pipelines/keras2onnx_application_tests.yml +++ b/ci_build/azure_pipelines/keras2onnx_application_tests.yml @@ -56,7 +56,7 @@ jobs: INSTALL_NUMPY: pip install numpy==1.19.0 NIGHTLY_BUILD_TEST: python run_all_v2.py - Python38-onnx1.11-tf2.8: + Python37-onnx1.11-tf2.8: python.version: '3.7' ONNX_PATH: onnx==1.11.0 INSTALL_KERAS: @@ -115,17 +115,17 @@ jobs: INSTALL_NUMPY: pip install numpy==1.19.0 NIGHTLY_BUILD_TEST: python run_all_v2.py --exclude "test_keras_applications_v2.py" - Python38-tf2.x: + Python38-onnx1.11-tf2.9: python.version: '3.8' ONNX_PATH: onnx==1.11.0 INSTALL_KERAS: - UNINSTALL_KERAS: pip uninstall keras -y - INSTALL_TENSORFLOW: pip install tensorflow==2.5.0 - INSTALL_ORT: pip install onnxruntime==1.9.0 + UNINSTALL_KERAS: + INSTALL_TENSORFLOW: pip install tensorflow==2.9.0 + INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_KERAS_RESNET: pip install keras-resnet INSTALL_TRANSFORMERS: pip install transformers==3.4.0 - INSTALL_NUMPY: pip install numpy==1.19.0 - NIGHTLY_BUILD_TEST: python run_all_v2.py --exclude "test_keras_applications_v2.py" + INSTALL_NUMPY: + NIGHTLY_BUILD_TEST: python run_all_v2.py steps: - template: 'templates/keras2onnx_application_tests.yml' From f2782493820b35cef3f7641743b839f4f87cfb68 Mon Sep 17 00:00:00 2001 From: SheSung Date: Tue, 5 Jul 2022 11:23:25 +0800 Subject: [PATCH 23/40] L2_NORMALIZATION support for tflite (#1989) L2_NORMALIZATION support for tflite Signed-off-by: Shesung Co-authored-by: Deyu Huang --- tests/test_backend.py | 8 ++++++++ tf2onnx/onnx_opset/math.py | 12 ++++++++++++ tf2onnx/tflite_handlers/tfl_direct.py | 1 + 3 files changed, 21 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index c0e822a77..2c1feb362 100755 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -5873,6 +5873,14 @@ def func(x): x_val = np.array([0.5, 1.0, -0.5, -1.0], dtype=np.float32).reshape((2, 2)) self._run_test_case(func, [_OUTPUT], {_INPUT: x_val}) + @skip_tfjs("not supported in tfjs") + def test_l2normalization(self): + def func(x): + op_ = tf.math.l2_normalize(x) + return tf.identity(op_, name=_TFOUTPUT) + + x_val = make_xval([3, 4]) + self._run_test_case(func, [_OUTPUT], {_INPUT: x_val}) if __name__ == '__main__': unittest_main() diff --git a/tf2onnx/onnx_opset/math.py b/tf2onnx/onnx_opset/math.py index b726e96a2..bdebd1fa6 100644 --- a/tf2onnx/onnx_opset/math.py +++ b/tf2onnx/onnx_opset/math.py @@ -826,3 +826,15 @@ class HardSwish: @classmethod def version_14(cls, ctx, node, **kwargs): pass + + +@tf_op(["L2Normalization"], onnx_op="LpNormalization") +class L2Normalization: + @classmethod + def version_1(cls, ctx, node, **kwargs): + axis = node.get_attr_value("axis") + if axis is None: + # by default use the last dim + axis = -1 + node.set_attr("axis", axis) + node.set_attr("p", 2) diff --git a/tf2onnx/tflite_handlers/tfl_direct.py b/tf2onnx/tflite_handlers/tfl_direct.py index c79f22811..1d10729a9 100644 --- a/tf2onnx/tflite_handlers/tfl_direct.py +++ b/tf2onnx/tflite_handlers/tfl_direct.py @@ -88,6 +88,7 @@ @tfl_op("TFL_RFFT2D", tf_op="RFFT2D") @tfl_op("TFL_COMPLEX_ABS", tf_op="ComplexAbs") @tfl_op("TFL_HARD_SWISH", tf_op="HardSwish") +@tfl_op("TFL_L2_NORMALIZATION", tf_op="L2Normalization") class TflDirectOp: @classmethod def to_tf(cls, ctx, node, **kwargs): From fa0b6cf9ae966a1b96f78b9e6c32f63c4e8689ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Volhejn?= <8401624+vvolhejn@users.noreply.github.com> Date: Wed, 6 Jul 2022 06:06:20 +0200 Subject: [PATCH 24/40] Replace deprecated `np.object` with `object` (#1990) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace deprecated `np.object` with `object` Signed-off-by: Václav Volhejn Co-authored-by: Deyu Huang --- tests/backend_test_base.py | 2 +- tests/run_pretrained_models.py | 2 +- tests/test_backend.py | 10 +++++----- tests/test_internals.py | 8 ++++---- tf2onnx/custom_opsets/string_ops.py | 6 +++--- tf2onnx/graph.py | 2 +- tf2onnx/optimizer/reshape_optimizer.py | 2 +- tf2onnx/symbolic_executor.py | 4 ++-- tf2onnx/tf_utils.py | 6 +++--- tf2onnx/tflite_utils.py | 2 +- tf2onnx/utils.py | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/backend_test_base.py b/tests/backend_test_base.py index 9d08c306a..f39c398b1 100644 --- a/tests/backend_test_base.py +++ b/tests/backend_test_base.py @@ -112,7 +112,7 @@ def assert_results_equal(self, expected, actual, rtol, atol, mtol=None, check_value=True, check_shape=True, check_dtype=True): for expected_val, actual_val in zip(expected, actual): if check_value: - if expected_val.dtype == np.object: + if expected_val.dtype == object: # TFLite pads strings with nul bytes decode = np.vectorize(lambda x: x.replace(b'\x00', b'').decode('UTF-8')) expected_val_str = decode(expected_val) diff --git a/tests/run_pretrained_models.py b/tests/run_pretrained_models.py index 29f88af24..78f36a79c 100644 --- a/tests/run_pretrained_models.py +++ b/tests/run_pretrained_models.py @@ -525,7 +525,7 @@ def run_tflite(): inputs[k] = np_value.astype(expected_dtype) else: if expected_dtype == "string": - inputs[k] = self.make_input(v).astype(np.str).astype(np.object) + inputs[k] = self.make_input(v).astype(np.str).astype(object) else: inputs[k] = self.make_input(v).astype(expected_dtype) diff --git a/tests/test_backend.py b/tests/test_backend.py index 2c1feb362..521978870 100755 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -5230,7 +5230,7 @@ def func(value, filters, output_shape): def test_hashtable_lookup(self): filnm = "vocab.tmp" words = ["apple", "pear", "banana", "cherry", "grape"] - query = np.array(['cherry'], dtype=np.object) + query = np.array(['cherry'], dtype=object) with open(filnm, "w") as f: for word in words: f.write(word + "\n") @@ -5247,7 +5247,7 @@ def func(query_holder): def test_hashtable_lookup_const(self): filnm = "vocab.tmp" words = ["apple", "pear", "banana", "cherry ♥", "grape"] - query_val = np.array(['cherry ♥', 'banana'], dtype=np.object).reshape((1, 2, 1)) + query_val = np.array(['cherry ♥', 'banana'], dtype=object).reshape((1, 2, 1)) with open(filnm, "w", encoding='UTF-8') as f: for word in words: f.write(word + "\n") @@ -5264,7 +5264,7 @@ def func(): def test_hashtable_size(self): filnm = "vocab.tmp" words = ["apple", "pear", "banana", "cherry", "grape"] - query = np.array(['cherry'], dtype=np.object) + query = np.array(['cherry'], dtype=object) with open(filnm, "w") as f: for word in words: f.write(word + "\n") @@ -5853,10 +5853,10 @@ def func(x): return tf.identity(op_, name=_TFOUTPUT) # tf gets this wrong and returns fp32 instead of int - x_val = np.array("123", dtype=np.object) + x_val = np.array("123", dtype=object) self._run_test_case(func, [_OUTPUT], {_INPUT: x_val}) - x_val = np.array("123.1", dtype=np.object) + x_val = np.array("123.1", dtype=object) # can't check the values because in onnx they are padded with 0, in tf they are not self._run_test_case(func, [_OUTPUT], {_INPUT: x_val}, check_value=False) diff --git a/tests/test_internals.py b/tests/test_internals.py index acd91d0cd..732616a9f 100644 --- a/tests/test_internals.py +++ b/tests/test_internals.py @@ -107,10 +107,10 @@ def test_insert_node2(self): def test_make_const_string(self): graph_proto = self.sample_net() g = GraphUtil.create_graph_from_onnx_graph(graph_proto) - arr1 = np.array("test", np.object) - arr2 = np.array([["A", "B"], ["C", "D"]], np.object) - arr3 = np.array(b"test", np.object) - arr4 = np.array([[b"A", b"B"], [b"C", b"D"]], np.object) + arr1 = np.array("test", object) + arr2 = np.array([["A", "B"], ["C", "D"]], object) + arr3 = np.array(b"test", object) + arr4 = np.array([[b"A", b"B"], [b"C", b"D"]], object) const1 = g.make_const("const1", arr1) const2 = g.make_const("const2", arr2) const3 = g.make_const("const3", arr3) diff --git a/tf2onnx/custom_opsets/string_ops.py b/tf2onnx/custom_opsets/string_ops.py index 24b854bc3..31fb0e363 100644 --- a/tf2onnx/custom_opsets/string_ops.py +++ b/tf2onnx/custom_opsets/string_ops.py @@ -53,8 +53,8 @@ def version_1(cls, ctx, node, **kwargs): rewrite = node.get_attr_str("rewrite") utils.make_sure(node.get_attr_value("replace_global") != 0, "Can not convert StaticRegexReplace if replace_global is False") - pattern_node = ctx.make_const(utils.make_name("pattern"), np.array([pattern], np.object)) - rewrite_node = ctx.make_const(utils.make_name("rewrite"), np.array([rewrite], np.object)) + pattern_node = ctx.make_const(utils.make_name("pattern"), np.array([pattern], object)) + rewrite_node = ctx.make_const(utils.make_name("rewrite"), np.array([rewrite], object)) del node.attr["pattern"] del node.attr["rewrite"] del node.attr["replace_global"] @@ -69,7 +69,7 @@ def version_1(cls, ctx, node, **kwargs): if separator is None: separator = b'' separator = separator.decode('UTF-8') - separator_node = ctx.make_const(utils.make_name("separator"), np.array([separator], np.object)) + separator_node = ctx.make_const(utils.make_name("separator"), np.array([separator], object)) axis_node = ctx.make_const(utils.make_name("axis"), np.array([0], np.int64)) inps_with_shapes = [i for i in node.input if ctx.get_shape(i) != []] shape_node = None diff --git a/tf2onnx/graph.py b/tf2onnx/graph.py index 32b8c69e3..e98eea9db 100644 --- a/tf2onnx/graph.py +++ b/tf2onnx/graph.py @@ -582,7 +582,7 @@ def make_const(self, name, np_val, skip_conversion=False, raw=True): raw: whether to store data at field of raw_data or the specific field according to its dtype """ np_val_flat = np_val.flatten() - is_bytes = np_val.dtype == np.object and len(np_val_flat) > 0 and isinstance(np_val_flat[0], bytes) + is_bytes = np_val.dtype == object and len(np_val_flat) > 0 and isinstance(np_val_flat[0], bytes) if raw and not is_bytes: onnx_tensor = numpy_helper.from_array(np_val, name) else: diff --git a/tf2onnx/optimizer/reshape_optimizer.py b/tf2onnx/optimizer/reshape_optimizer.py index 9eac9929c..f2ff6aa72 100644 --- a/tf2onnx/optimizer/reshape_optimizer.py +++ b/tf2onnx/optimizer/reshape_optimizer.py @@ -54,7 +54,7 @@ def _optimize_reshape(self, node, graph): symbolic_shape.append(SymbolicTensorElement.from_variable(i)) else: symbolic_shape.append(SymbolicTensorElement.from_const(d)) - feed_dict[n.output[0]] = np.array(symbolic_shape, np.object) + feed_dict[n.output[0]] = np.array(symbolic_shape, object) try: symbolic_res = SymbolicExecutor(graph).compute_outputs([node.input[1]], feed_dict) except SymbolicExecutionException: diff --git a/tf2onnx/symbolic_executor.py b/tf2onnx/symbolic_executor.py index 567147218..e8c8a5e6e 100644 --- a/tf2onnx/symbolic_executor.py +++ b/tf2onnx/symbolic_executor.py @@ -136,7 +136,7 @@ def compute_squeeze_unsqueeze(self, node, feed_dict): def compute_cast(self, node, feed_dict): inp = feed_dict[node.input[0]] - if inp.dtype == np.object: + if inp.dtype == object: return [inp] np_dtype = utils.ONNX_TO_NUMPY_DTYPE[node.get_attr("to").i] return [inp.astype(np_dtype)] @@ -181,7 +181,7 @@ def compute_concat(self, node, feed_dict): def compute_gather(self, node, feed_dict): data = feed_dict[node.input[0]] indices = feed_dict[node.input[1]] - if indices.dtype == np.object: + if indices.dtype == object: raise SymbolicExecutionException("Gather requires non-symbolic indices") axis = node.get_attr_value("axis", 0) return [np.take(data, indices, axis=axis)] diff --git a/tf2onnx/tf_utils.py b/tf2onnx/tf_utils.py index 5243b3a52..54abc7071 100644 --- a/tf2onnx/tf_utils.py +++ b/tf2onnx/tf_utils.py @@ -50,15 +50,15 @@ def tf_to_onnx_tensor(tensor, name=""): """Convert tensorflow tensor to onnx tensor.""" np_data = get_tf_tensor_data(tensor) - if np_data.dtype == np.object: + if np_data.dtype == object: # assume np_data is string, numpy_helper.from_array accepts ndarray, # in which each item is of str while the whole dtype is of object. try: # Faster but fails on Unicode - np_data = np_data.astype(np.str).astype(np.object) + np_data = np_data.astype(np.str).astype(object) except UnicodeDecodeError: decode = np.vectorize(lambda x: x.decode('UTF-8')) - np_data = decode(np_data).astype(np.object) + np_data = decode(np_data).astype(object) except: # pylint: disable=bare-except raise RuntimeError("Not support type: {}".format(type(np_data.flat[0]))) return numpy_helper.from_array(np_data, name=name) diff --git a/tf2onnx/tflite_utils.py b/tf2onnx/tflite_utils.py index 111f162f4..6e3f2d024 100644 --- a/tf2onnx/tflite_utils.py +++ b/tf2onnx/tflite_utils.py @@ -271,7 +271,7 @@ def read_int(offset): string_list = [] for i in range(count): string_list.append(buffer_bytes[offset_list[i]:offset_list[i+1]].decode("utf-8")) - return numpy_helper.from_array(np.array(string_list, dtype=np.object).reshape(shape)) + return numpy_helper.from_array(np.array(string_list, dtype=object).reshape(shape)) def op_has_scalar_output(input_shapes, optype, attr): diff --git a/tf2onnx/utils.py b/tf2onnx/utils.py index 9f3ba095d..ac6bcfcfa 100644 --- a/tf2onnx/utils.py +++ b/tf2onnx/utils.py @@ -45,7 +45,7 @@ onnx_pb.TensorProto.BOOL: np.bool, onnx_pb.TensorProto.COMPLEX64: np.complex64, onnx_pb.TensorProto.COMPLEX128: np.complex128, - onnx_pb.TensorProto.STRING: np.object, + onnx_pb.TensorProto.STRING: object, } # From 9ce72be41ba8db4629e4647624998f2345c37f91 Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Fri, 8 Jul 2022 18:35:44 +0800 Subject: [PATCH 25/40] Add --outputs_as_nchw option to transpose output to from nhwc to nchw (#1979) * add output_as_nchw Signed-off-by: Deyu Huang * fix node replace logic Signed-off-by: Deyu Huang * add tests for outputs as nchw Signed-off-by: Deyu Huang * add it into function and doc Signed-off-by: Deyu Huang * fix output_names_with_port range Signed-off-by: Deyu Huang * fix the input_as_nchw description Signed-off-by: Deyu Huang * change tests name Signed-off-by: Deyu Huang --- README.md | 26 +++++++++++-------- tests/backend_test_base.py | 20 ++++++++++++-- tests/test_backend.py | 13 +++++++++- tf2onnx/convert.py | 44 ++++++++++++++++++++----------- tf2onnx/tfonnx.py | 53 ++++++++++++++++++++++++++++---------- 5 files changed, 114 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index ad640e21e..bd4753e6b 100644 --- a/README.md +++ b/README.md @@ -292,8 +292,8 @@ import tf2onnx model_proto, external_tensor_storage = tf2onnx.convert.from_keras(model, input_signature=None, opset=None, custom_ops=None, custom_op_handlers=None, custom_rewriter=None, - inputs_as_nchw=None, extra_opset=None shape_override=None, - target=None, large_model=False, output_path=None) + inputs_as_nchw=None, outputs_as_nchw=None, extra_opset=None, + shape_override=None, target=None, large_model=False, output_path=None) Args: model: the tf.keras model we want to convert @@ -307,7 +307,8 @@ model_proto, external_tensor_storage = tf2onnx.convert.from_keras(model, custom_rewriter: list of custom graph rewriters extra_opset: list of extra opset's, for example the opset's used by custom ops shape_override: dict with inputs that override the shapes given by tensorflow - inputs_as_nchw: transpose inputs in list from nchw to nhwc + inputs_as_nchw: transpose inputs in list from nhwc to nchw + outputs_as_nchw: transpose outputs in list from nhwc to nchw large_model: use the ONNX external tensor storage format output_path: save model to output_path @@ -323,8 +324,8 @@ import tf2onnx model_proto, external_tensor_storage = tf2onnx.convert.from_function(function, input_signature=None, opset=None, custom_ops=None, - custom_op_handlers=None, custom_rewriter=None, - inputs_as_nchw=None, extra_opset=None, shape_override=None, + custom_op_handlers=None, custom_rewriter=None, inputs_as_nchw=None, + outputs_as_nchw=None, extra_opset=None, shape_override=None, target=None, large_model=False, output_path=None) Args: @@ -339,7 +340,8 @@ model_proto, external_tensor_storage = tf2onnx.convert.from_function(function, custom_rewriter: list of custom graph rewriters extra_opset: list of extra opset's, for example the opset's used by custom ops shape_override: dict with inputs that override the shapes given by tensorflow - inputs_as_nchw: transpose inputs in list from nchw to nhwc + inputs_as_nchw: transpose inputs in list from nhwc to nchw + outputs_as_nchw: transpose outputs in list from nhwc to nchw large_model: use the ONNX external tensor storage format output_path: save model to output_path @@ -354,7 +356,7 @@ import tf2onnx model_proto, external_tensor_storage = tf2onnx.convert.from_graph_def(graph_def, name=None, input_names=None, output_names=None, opset=None, custom_ops=None, custom_op_handlers=None, custom_rewriter=None, - inputs_as_nchw=None, extra_opset=None, + inputs_as_nchw=None, outputs_as_nchw=None, extra_opset=None, shape_override=None, target=None, large_model=False, output_path=None) @@ -369,7 +371,8 @@ model_proto, external_tensor_storage = tf2onnx.convert.from_graph_def(graph_def, custom_rewriter: list of custom graph rewriters extra_opset: list of extra opset's, for example the opset's used by custom ops shape_override: dict with inputs that override the shapes given by tensorflow - inputs_as_nchw: transpose inputs in list from nchw to nhwc + inputs_as_nchw: transpose inputs in list from nhwc to nchw + outputs_as_nchw: transpose outputs in list from nhwc to nchw large_model: use the ONNX external tensor storage format output_path: save model to output_path @@ -383,8 +386,8 @@ import tf2onnx model_proto, external_tensor_storage = tf2onnx.convert.from_tflite(tflite_path, input_names=None, output_names=None, opset=None, custom_ops=None, custom_op_handlers=None, - custom_rewriter=None, inputs_as_nchw=None, extra_opset=None, shape_override=None, target=None, - large_model=False, output_path=None): + custom_rewriter=None, inputs_as_nchw=None, outputs_as_nchw=None, extra_opset=None, + shape_override=None, target=None, large_model=False, output_path=None): Args: tflite_path: the tflite model file full path @@ -396,7 +399,8 @@ model_proto, external_tensor_storage = tf2onnx.convert.from_tflite(tflite_path, runtime can still open the model. Type is a dictionary `{op name: domain}`. custom_op_handlers: dictionary of custom ops handlers custom_rewriter: list of custom graph rewriters - inputs_as_nchw: transpose inputs in list from nchw to nhwc + inputs_as_nchw: transpose inputs in list from nhwc to nchw + outputs_as_nchw: transpose outputs in list from nhwc to nchw extra_opset: list of extra opset's, for example the opset's used by custom ops shape_override: dict with inputs that override the shapes given by tensorflow target: list of workarounds applied to help certain platforms diff --git a/tests/backend_test_base.py b/tests/backend_test_base.py index f39c398b1..38cc52dcf 100644 --- a/tests/backend_test_base.py +++ b/tests/backend_test_base.py @@ -20,6 +20,7 @@ import onnx from common import get_test_config from tfjs_runner import run_tfjs +from tf2onnx import constants from tf2onnx import utils from tf2onnx.tfonnx import process_tf_graph from tf2onnx import optimizer @@ -366,6 +367,7 @@ def run_test_case(self, func, feed_dict, input_names_with_port, output_names_wit graph_def_path = os.path.join(self.test_data_directory, self._testMethodName + "_after_tf_optimize.pb") utils.save_protobuf(graph_def_path, graph_def) self.logger.debug("created file %s", graph_def_path) + tfl_process_args = process_args.copy() if test_tfjs: tfjs_path = self.convert_to_tfjs(graph_def_path, output_names_with_port) @@ -395,6 +397,10 @@ def run_test_case(self, func, feed_dict, input_names_with_port, output_names_wit g = optimizer.optimize_graph(g, catch_errors=False) actual = self.run_backend(g, output_names_with_port, onnx_feed_dict, large_model, use_custom_ops=use_custom_ops) + if 'outputs_as_nchw' in tfl_process_args: + for output_name in tfl_process_args['outputs_as_nchw']: + i = output_names_with_port.index(output_name) + actual[i] = np.transpose(actual[i], constants.NCHW_TO_NHWC) self.assert_results_equal(expected, actual, rtol, atol, mtol, check_value, check_shape, check_dtype) self.assert_shapes_correct(g, self.config.allow_missing_shapes, not self.config.skip_onnx_checker) @@ -410,12 +416,14 @@ def run_test_case(self, func, feed_dict, input_names_with_port, output_names_wit if run_tfl_consistency_test: self.assert_results_equal(expected, tfl_res, rtol, atol, mtol, check_value, check_shape, check_dtype) - tfl_process_args = process_args.copy() if 'inputs_as_nchw' in tfl_process_args: nchw_inps_with_port = tfl_process_args['inputs_as_nchw'] tfl_process_args['inputs_as_nchw'] = [i.split(':')[0] for i in nchw_inps_with_port] input_names_without_port = [inp.split(':')[0] for inp in feed_dict.keys()] - + if 'outputs_as_nchw' in tfl_process_args: + nchw_outps_with_port = tfl_process_args['outputs_as_nchw'] + tfl_process_args['outputs_as_nchw'] = [i.split(':')[0] for i in nchw_outps_with_port] + output_names_with_port = [i.split(':')[0] for i in nchw_outps_with_port] g = process_tf_graph(None, opset=self.config.opset, input_names=input_names_without_port, output_names=tfl_outputs, @@ -427,6 +435,10 @@ def run_test_case(self, func, feed_dict, input_names_with_port, output_names_wit onnx_feed_dict_without_port = {k.split(':')[0]: v for k, v in onnx_feed_dict.items()} onnx_tfl_res = self.run_backend(g, tfl_outputs, onnx_feed_dict_without_port, postfix="_from_tflite", use_custom_ops=use_custom_ops) + if 'outputs_as_nchw' in tfl_process_args: + for output_name in tfl_process_args['outputs_as_nchw']: + i = output_names_with_port.index(output_name) + onnx_tfl_res[i] = np.transpose(onnx_tfl_res[i], constants.NCHW_TO_NHWC) self.assert_results_equal(tfl_res, onnx_tfl_res, rtol, atol, mtol, check_value, check_shape, check_dtype) self.assert_shapes_correct(g, self.config.allow_missing_shapes, not self.config.skip_onnx_checker) @@ -456,6 +468,10 @@ def run_test_case(self, func, feed_dict, input_names_with_port, output_names_wit g = optimizer.optimize_graph(g) onnx_tfjs_res = self.run_backend(g, None, onnx_feed_dict, large_model, postfix="_from_tfjs", use_custom_ops=use_custom_ops) + if 'outputs_as_nchw' in tfl_process_args: + for output_name in tfl_process_args['outputs_as_nchw']: + i = output_names_with_port.index(output_name) + onnx_tfjs_res[i] = np.transpose(onnx_tfjs_res[i], constants.NCHW_TO_NHWC) self.assert_results_equal(tfjs_res, onnx_tfjs_res, rtol, atol, mtol, check_value, check_shape, check_dtype=False) diff --git a/tests/test_backend.py b/tests/test_backend.py index 521978870..fe50e0591 100755 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -712,7 +712,7 @@ def func(x): graph_validator=lambda g: (check_op_count(g, "RandomUniform", 0) and check_op_count(g, "RandomUniformLike", 0))) - def test_conv2d_with_input_transpose(self): + def test_inputs_as_nchw_arg(self): x_shape = [2, 32, 32, 3] kernel_shape = [3, 3, 3, 3] x_val = make_xval(x_shape) @@ -725,6 +725,17 @@ def func(x): process_args={"inputs_as_nchw": [_INPUT]}, onnx_feed_dict={_INPUT: x_val_for_onnx}) + def test_outputs_as_nchw_arg(self): + x_shape = [2, 32, 32, 3] + kernel_shape = [3, 3, 3, 3] + x_val = make_xval(x_shape) + def func(x): + kernel = tf.constant(make_xval(kernel_shape), dtype=tf.float32, name='kernel') + conv = tf.nn.conv2d(x, kernel, strides=[1, 1, 1, 1], padding="SAME") + return tf.identity(conv, name=_TFOUTPUT) + self._run_test_case(func, [_OUTPUT], {_INPUT: x_val}, rtol=1e-05, + process_args={"outputs_as_nchw": [_OUTPUT]}) + @skip_tflite("TFlite adds ops that obscure pattern") @check_tf_min_version("1.15") def test_conv1d_dilations_rewriter(self): diff --git a/tf2onnx/convert.py b/tf2onnx/convert.py index 0a1069496..32c28f0bc 100644 --- a/tf2onnx/convert.py +++ b/tf2onnx/convert.py @@ -86,11 +86,12 @@ def get_args(): # experimental parser.add_argument("--inputs-as-nchw", help="transpose inputs as from nhwc to nchw") + parser.add_argument("--outputs-as-nchw", help="transpose outputs as from nhwc to nchw") args = parser.parse_args() args.shape_override = None if args.input: - # for backward compativility + # for backward compatibility args.graphdef = args.input if args.graphdef or args.checkpoint: if not args.inputs or not args.outputs: @@ -112,6 +113,8 @@ def get_args(): args.rename_inputs = args.rename_inputs.split(",") if args.inputs_as_nchw: args.inputs_as_nchw = args.inputs_as_nchw.split(",") + if args.outputs_as_nchw: + args.outputs_as_nchw = args.outputs_as_nchw.split(",") if args.target: args.target = args.target.split(",") if args.signature_def: @@ -275,6 +278,7 @@ def main(): input_names=inputs, output_names=outputs, inputs_as_nchw=args.inputs_as_nchw, + outputs_as_nchw=args.outputs_as_nchw, large_model=args.large_model, tensors_to_rename=tensors_to_rename, ignore_default=args.ignore_default, @@ -356,8 +360,8 @@ def _is_legacy_keras_model(model): def _from_keras_tf1(model, opset=None, custom_ops=None, custom_op_handlers=None, custom_rewriter=None, - inputs_as_nchw=None, extra_opset=None, shape_override=None, target=None, - large_model=False, output_path=None): + inputs_as_nchw=None, outputs_as_nchw=None, extra_opset=None, shape_override=None, + target=None, large_model=False, output_path=None): """from_keras for tf 1.15""" input_names = [t.name for t in model.inputs] output_names = [t.name for t in model.outputs] @@ -392,6 +396,7 @@ def _from_keras_tf1(model, opset=None, custom_ops=None, custom_op_handlers=None, input_names=input_names, output_names=output_names, inputs_as_nchw=inputs_as_nchw, + outputs_as_nchw=outputs_as_nchw, large_model=large_model, tensors_to_rename=tensors_to_rename, initialized_tables=initialized_tables, @@ -401,7 +406,7 @@ def _from_keras_tf1(model, opset=None, custom_ops=None, custom_op_handlers=None, def from_keras(model, input_signature=None, opset=None, custom_ops=None, custom_op_handlers=None, - custom_rewriter=None, inputs_as_nchw=None, extra_opset=None, shape_override=None, + custom_rewriter=None, inputs_as_nchw=None, outputs_as_nchw=None, extra_opset=None, shape_override=None, target=None, large_model=False, output_path=None, optimizers=None): """Returns a ONNX model_proto for a tf.keras model. @@ -417,7 +422,8 @@ def from_keras(model, input_signature=None, opset=None, custom_ops=None, custom_ custom_rewriter: list of custom graph rewriters extra_opset: list of extra opset's, for example the opset's used by custom ops shape_override: dict with inputs that override the shapes given by tensorflow - inputs_as_nchw: transpose inputs in list from nchw to nhwc + inputs_as_nchw: transpose inputs in list from nhwc to nchw + outputs_as_nchw: transpose outputs in list from nhwc to nchw large_model: use the ONNX external tensor storage format output_path: save model to output_path optimizers: list (subset) of tf2onnx optimizers if applying all optimizers is not desired. @@ -427,7 +433,7 @@ def from_keras(model, input_signature=None, opset=None, custom_ops=None, custom_ """ if LooseVersion(tf.__version__) < "2.0": return _from_keras_tf1(model, opset, custom_ops, custom_op_handlers, custom_rewriter, inputs_as_nchw, - extra_opset, shape_override, target, large_model, output_path) + outputs_as_nchw, extra_opset, shape_override, target, large_model, output_path) old_out_names = _rename_duplicate_keras_model_names(model) from tensorflow.python.keras.saving import saving_utils as _saving_utils # pylint: disable=import-outside-toplevel @@ -500,6 +506,7 @@ def wrap_call(*args, training=False, **kwargs): input_names=input_names, output_names=output_names, inputs_as_nchw=inputs_as_nchw, + outputs_as_nchw=outputs_as_nchw, large_model=large_model, tensors_to_rename=tensors_to_rename, initialized_tables=initialized_tables, @@ -509,8 +516,8 @@ def wrap_call(*args, training=False, **kwargs): def from_function(function, input_signature=None, opset=None, custom_ops=None, custom_op_handlers=None, - custom_rewriter=None, inputs_as_nchw=None, extra_opset=None, shape_override=None, target=None, - large_model=False, output_path=None): + custom_rewriter=None, inputs_as_nchw=None, outputs_as_nchw=None, extra_opset=None, + shape_override=None, target=None, large_model=False, output_path=None): """Returns a ONNX model_proto for a tf.function. Args: @@ -525,7 +532,8 @@ def from_function(function, input_signature=None, opset=None, custom_ops=None, c custom_rewriter: list of custom graph rewriters extra_opset: list of extra opset's, for example the opset's used by custom ops shape_override: dict with inputs that override the shapes given by tensorflow - inputs_as_nchw: transpose inputs in list from nchw to nhwc + inputs_as_nchw: transpose inputs in list from nhwc to nchw + outputs_as_nchw: transpose outputs in list from nhwc to nchw large_model: use the ONNX external tensor storage format output_path: save model to output_path @@ -564,6 +572,7 @@ def from_function(function, input_signature=None, opset=None, custom_ops=None, c input_names=input_names, output_names=output_names, inputs_as_nchw=inputs_as_nchw, + outputs_as_nchw=outputs_as_nchw, large_model=large_model, tensors_to_rename=tensors_to_rename, initialized_tables=initialized_tables, @@ -573,8 +582,9 @@ def from_function(function, input_signature=None, opset=None, custom_ops=None, c def from_graph_def(graph_def, name=None, input_names=None, output_names=None, opset=None, custom_ops=None, - custom_op_handlers=None, custom_rewriter=None, inputs_as_nchw=None, extra_opset=None, - shape_override=None, target=None, large_model=False, tensors_to_rename=None, output_path=None): + custom_op_handlers=None, custom_rewriter=None, inputs_as_nchw=None, outputs_as_nchw=None, + extra_opset=None, shape_override=None, target=None, large_model=False, + tensors_to_rename=None, output_path=None): """Returns a ONNX model_proto for a tensorflow graphdef. Args: @@ -591,7 +601,8 @@ def from_graph_def(graph_def, name=None, input_names=None, output_names=None, op custom_rewriter: list of custom graph rewriters extra_opset: list of extra opset's, for example the opset's used by custom ops shape_override: dict with inputs that override the shapes given by tensorflow - inputs_as_nchw: transpose inputs in list from nchw to nhwc + inputs_as_nchw: transpose inputs in list from nhwc to nchw + outputs_as_nchw: transpose outputs in list from nhwc to nchw large_model: use the ONNX external tensor storage format output_path: save model to output_path @@ -628,6 +639,7 @@ def from_graph_def(graph_def, name=None, input_names=None, output_names=None, op input_names=input_names, output_names=output_names, inputs_as_nchw=inputs_as_nchw, + outputs_as_nchw=outputs_as_nchw, large_model=large_model, tensors_to_rename=tensors_to_rename, initialized_tables=initialized_tables, @@ -637,8 +649,8 @@ def from_graph_def(graph_def, name=None, input_names=None, output_names=None, op def from_tflite(tflite_path, input_names=None, output_names=None, opset=None, custom_ops=None, custom_op_handlers=None, - custom_rewriter=None, inputs_as_nchw=None, extra_opset=None, shape_override=None, target=None, - large_model=False, output_path=None): + custom_rewriter=None, inputs_as_nchw=None, outputs_as_nchw=None, extra_opset=None, shape_override=None, + target=None, large_model=False, output_path=None): """Returns a ONNX model_proto for a tflite model file. Args: @@ -651,7 +663,8 @@ def from_tflite(tflite_path, input_names=None, output_names=None, opset=None, cu runtime can still open the model. Type is a dictionary `{op name: domain}`. custom_op_handlers: dictionary of custom ops handlers custom_rewriter: list of custom graph rewriters - inputs_as_nchw: transpose inputs in list from nchw to nhwc + inputs_as_nchw: transpose inputs in list from nhwc to nchw + outputs_as_nchw: transpose outputs in list from nhwc to nchw extra_opset: list of extra opset's, for example the opset's used by custom ops shape_override: dict with inputs that override the shapes given by tensorflow target: list of workarounds applied to help certain platforms @@ -680,6 +693,7 @@ def from_tflite(tflite_path, input_names=None, output_names=None, opset=None, cu input_names=input_names, output_names=output_names, inputs_as_nchw=inputs_as_nchw, + outputs_as_nchw=outputs_as_nchw, large_model=large_model, tensors_to_rename=None, initialized_tables=None, diff --git a/tf2onnx/tfonnx.py b/tf2onnx/tfonnx.py index 1a351cfcb..c2c881e77 100644 --- a/tf2onnx/tfonnx.py +++ b/tf2onnx/tfonnx.py @@ -329,6 +329,29 @@ def transpose_inputs(ctx, inputs_as_nchw): ops.append(node) ctx.reset_nodes(ops) +def transpose_outputs(ctx, outputs_as_nchw): + """Insert a transpose from NHWC to NCHW on model output on users request.""" + ops = [] + for node in ctx.get_nodes(): + for output_name in node.output: + if output_name in outputs_as_nchw: + shape = ctx.get_shape(output_name) + if len(shape) != len(constants.NHWC_TO_NCHW): + logger.warning("transpose_output for %s: shape must be rank 4, ignored" % output_name) + ops.append(node) + continue + # insert transpose + op_name = utils.make_name(node.name) + transpose = ctx.insert_new_node_on_output("Transpose", node.input[0], name=op_name) + transpose.set_attr("perm", constants.NHWC_TO_NCHW) + ctx.copy_shape(node.output[0], transpose.output[0]) + ctx.set_shape(transpose.output[0], np.array(shape)[constants.NHWC_TO_NCHW]) + ctx.set_shape(output_name, np.array(shape)[constants.NHWC_TO_NCHW]) + ops.append(transpose) + ops.append(node) + continue + ops.append(node) + ctx.reset_nodes(ops) def topological_sort(g, continue_on_error): ops = g.get_nodes() @@ -376,7 +399,7 @@ def run_rewriters(g, funcs, continue_on_error): def process_tf_graph(tf_graph, continue_on_error=False, verbose=False, target=None, opset=None, custom_op_handlers=None, custom_rewriter=None, - extra_opset=None, shape_override=None, inputs_as_nchw=None, + extra_opset=None, shape_override=None, inputs_as_nchw=None, outputs_as_nchw=None, input_names=None, output_names=None, ignore_default=None, use_default=None, is_subgraph=False, const_node_values=None, tensors_to_rename=None, initialized_tables=None, tflite_path=None, dequantize=False, tfjs_path=None): @@ -391,7 +414,8 @@ def process_tf_graph(tf_graph, continue_on_error=False, verbose=False, target=No custom_rewriter: list of custom graph rewriters extra_opset: list of extra opset's, for example the opset's used by custom ops shape_override: dict with inputs that override the shapes given by tensorflow - inputs_as_nchw: transpose inputs in list from nchw to nhwc + inputs_as_nchw: transpose inputs in list from nhwc to nchw + outputs_as_nchw: transpose outputs in list from nhwc to nchw input_names: list of input node names in graph, input name format as node_name:port_id. Optional. output_names: list of output node names in graph, format is node_name:port_id. Optional for tflite. ignore_default: list of node names of PlaceholderWithDefault ops to change into Placeholder ops @@ -421,6 +445,8 @@ def process_tf_graph(tf_graph, continue_on_error=False, verbose=False, target=No clear_functions() if inputs_as_nchw is None: inputs_as_nchw = [] + if outputs_as_nchw is None: + outputs_as_nchw = [] is_tflite = False if tflite_path is not None: @@ -435,8 +461,8 @@ def process_tf_graph(tf_graph, continue_on_error=False, verbose=False, target=No for g in [main_g] + subgraphs: g.set_config(target, opset, extra_opset) - g = process_graphs(main_g, subgraphs, custom_op_handlers, inputs_as_nchw, continue_on_error, custom_rewriter, - initialized_tables, tensors_to_rename, is_tflite, dequantize) + g = process_graphs(main_g, subgraphs, custom_op_handlers, inputs_as_nchw, outputs_as_nchw, continue_on_error, + custom_rewriter, initialized_tables, tensors_to_rename, is_tflite, dequantize) return g @@ -476,24 +502,23 @@ def graphs_from_tf(tf_graph, input_names, output_names, shape_override=None, con return main_g, subgraphs -def process_graphs(main_g, subgraphs, custom_op_handlers, inputs_as_nchw, continue_on_error, custom_rewriter, - initialized_tables, tensors_to_rename, is_tflite=False, dequantize=False): - +def process_graphs(main_g, subgraphs, custom_op_handlers, inputs_as_nchw, outputs_as_nchw, continue_on_error, + custom_rewriter, initialized_tables, tensors_to_rename, is_tflite=False, dequantize=False): if tensors_to_rename is not None: main_g.rename_tensors(tensors_to_rename) inputs_as_nchw = [tensors_to_rename.get(t, t) for t in inputs_as_nchw] + outputs_as_nchw = [tensors_to_rename.get(t, t) for t in outputs_as_nchw] for g in subgraphs: - fg = process_parsed_graph(g, custom_op_handlers, inputs_as_nchw, continue_on_error, custom_rewriter, - initialized_tables, is_tflite, dequantize) + fg = process_parsed_graph(g, custom_op_handlers, inputs_as_nchw, outputs_as_nchw, continue_on_error, + custom_rewriter, initialized_tables, is_tflite, dequantize) set_function(fg.graph_name, fg) - g = process_parsed_graph(main_g, custom_op_handlers, inputs_as_nchw, continue_on_error, custom_rewriter, - initialized_tables, is_tflite, - dequantize) + g = process_parsed_graph(main_g, custom_op_handlers, inputs_as_nchw, outputs_as_nchw, continue_on_error, + custom_rewriter, initialized_tables, is_tflite, dequantize) return g -def process_parsed_graph(g, custom_op_handlers, inputs_as_nchw, continue_on_error, custom_rewriter, +def process_parsed_graph(g, custom_op_handlers, inputs_as_nchw, outputs_as_nchw, continue_on_error, custom_rewriter, initialized_tables, is_tflite=False, dequantize=False): op_cnt, attr_cnt = g.dump_node_statistics(include_attrs=True, include_subgraphs=False) @@ -549,6 +574,8 @@ def compat_handler(ctx, node, **kwargs): if inputs_as_nchw: transpose_inputs(g, inputs_as_nchw) + if outputs_as_nchw: + transpose_outputs(g, outputs_as_nchw) # pre-processing graph rewrites # bi-directional re-writer should be placed after single directional re-writer From e896723e410a59a600d1a73657f9965a3cbf2c3b Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Fri, 15 Jul 2022 14:16:58 +0800 Subject: [PATCH 26/40] Fix transpose split optimize attr when opset >=13 (#1996) * add tranposeOptimizer split for opset >= 13 Signed-off-by: Deyu Huang * add test Signed-off-by: Deyu Huang * fix comments Signed-off-by: Deyu Huang --- tests/test_optimizers.py | 51 ++++++++++++++++++------ tf2onnx/optimizer/transpose_optimizer.py | 12 +++++- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/tests/test_optimizers.py b/tests/test_optimizers.py index 180913640..594c625e2 100644 --- a/tests/test_optimizers.py +++ b/tests/test_optimizers.py @@ -145,7 +145,7 @@ def test_transpose_with_split(self, input_shape, perm, inner_perm): ((1, -1), (1, 1710), (1710,), [1, 0]), ((3, 1, 1, 5, -1), (3, 1, 1, 5, 6), (3, 5, 6), [0, 2, 3, 4, 1]), ]) - @check_opset_max_version(12, "split attribute changed to input in opset 13") + @check_opset_max_version(12, "split attribute changed to input since opset 13") def test_transpose_with_split_dynamic_shape(self, input_shape, specific_input, output_shape, perm): node1 = helper.make_node("Transpose", ["X"], ["Y"], perm=perm, name="trans") node2 = helper.make_node("Split", ["Y"], ["Z"], axis=1, split=[1], name="split") @@ -162,6 +162,31 @@ def test_transpose_with_split_dynamic_shape(self, input_shape, specific_input, o self.run_transpose_compare(["B"], {"X": np.random.randn(*specific_input).astype(np.float32)}, model_proto, remaining_transpose_num=0) + @parameterized.expand([ + ((3, 1, 1), (1, 1, 3), (1), [0, 2, 3, 1]), + ((256, 1, 1), (1, 1, 256), (1), [0, 2, 3, 1]) + ]) + @check_opset_min_version(13, "split attribute changed to input since opset 13") + def test_transpose_with_split_opset13(self, input_shape, output_shape, split_val, perm): + unsqueeze_axes = self._make_onnx_const(np.array([0], dtype=np.int64), "axes1") + unsqueeze = helper.make_node("Unsqueeze", ["X", "axes1"], ["Y"], name="unsqueeze") + trans = helper.make_node("Transpose", ["Y"], ["Z"], perm=perm, name="trans") + split_attr = self._make_onnx_const(np.array([split_val], dtype=np.int64), "split_attr") + split = helper.make_node("Split", ["Z", "split_attr"], ["A"], axis=0, name="split") + squeeze_axes = self._make_onnx_const(np.array([1], dtype=np.int64), "axes2") + squeeze = helper.make_node("Squeeze", ["A", "axes2"], ["B"], name="squeeze") + + graph = helper.make_graph( + [unsqueeze_axes, unsqueeze, trans, split_attr, split, squeeze_axes, squeeze], + "test_transpose_with_split_opset13", + [helper.make_tensor_value_info("X", TensorProto.FLOAT, input_shape)], + [helper.make_tensor_value_info("B", TensorProto.FLOAT, output_shape)], + ) + + model_proto = self.make_model(graph, producer_name="onnx-tests") + self.run_transpose_compare(["B"], {"X": np.random.randn(*input_shape).astype(np.float32)}, + model_proto, remaining_transpose_num=0) + @parameterized.expand([ ((2, 3, 4), [2, 0, 1], [1, 2, 0]), ((2, 3, 4, 5), [0, 2, 3, 1], [0, 3, 1, 2]), @@ -717,7 +742,7 @@ def test_transpose_sqrt(self, shape, perm_input, perm_output): ((1, 3, 4, 5), (4, 5, 3), [0, 2, 3, 1], [1, 2, 0]), ((1, 3, 4, 5, 6), (4, 5, 6, 3), [0, 2, 3, 4, 1], [1, 2, 3, 0]), ]) - @check_opset_max_version(12, "Squeeze/Unsqueeze changed in opset 13") + @check_opset_max_version(12, "Squeeze/Unsqueeze changed since opset 13") def test_transpose_with_squeeze1(self, input_shape, output_shape, perm, expected_perm): # squeeze the first dim node1 = helper.make_node("Transpose", ["X"], ["Y"], perm=perm, name="trans") @@ -768,7 +793,7 @@ def test_transpose_with_unsqueeze(self, input_shape, output_shape, perm, axes_va ((1, 3, 4, 5), (4, 5, 3), [0, 2, 3, 1], [1, 2, 0]), ((1, 3, 4, 5, 6), (4, 5, 6, 3), [0, 2, 3, 4, 1], [1, 2, 3, 0]), ]) - @check_opset_min_version(13, "Squeeze/Unsqueeze changed in opset 13") + @check_opset_min_version(13, "Squeeze/Unsqueeze changed since opset 13") def test_transpose_with_squeeze1_13(self, input_shape, output_shape, perm, expected_perm): # squeeze the first dim node1 = helper.make_node("Transpose", ["X"], ["Y"], perm=perm, name="trans") @@ -791,7 +816,7 @@ def test_transpose_with_squeeze1_13(self, input_shape, output_shape, perm, expec ((3, 4, 1, 5), (3, 5, 4), [0, 2, 3, 1], [0, 2, 1]), ((3, 4, 1, 5, 6), (3, 5, 6, 4), [0, 2, 3, 4, 1], [0, 2, 3, 1]), ]) - @check_opset_max_version(12, "Squeeze/Unsqueeze changed in opset 13") + @check_opset_max_version(12, "Squeeze/Unsqueeze changed since opset 13") def test_transpose_with_squeeze2(self, input_shape, output_shape, perm, expected_perm): # squeeze the second dim node1 = helper.make_node("Transpose", ["X"], ["Y"], perm=perm, name="trans") @@ -813,7 +838,7 @@ def test_transpose_with_squeeze2(self, input_shape, output_shape, perm, expected ((3, 4, 1, 5), (3, 5, 4), [0, 2, 3, 1], [0, 2, 1]), ((3, 4, 1, 5, 6), (3, 5, 6, 4), [0, 2, 3, 4, 1], [0, 2, 3, 1]), ]) - @check_opset_min_version(13, "Squeeze/Unsqueeze changed in opset 13") + @check_opset_min_version(13, "Squeeze/Unsqueeze changed since opset 13") def test_transpose_with_squeeze2_13(self, input_shape, output_shape, perm, expected_perm): # squeeze the second dim node1 = helper.make_node("Transpose", ["X"], ["Y"], perm=perm, name="trans") @@ -836,7 +861,7 @@ def test_transpose_with_squeeze2_13(self, input_shape, output_shape, perm, expec ((3, 1, 4, 5), (3, 4, 5), [0, 2, 3, 1]), ((3, 1, 4, 5, 6), (3, 4, 5, 6), [0, 2, 3, 4, 1]), ]) - @check_opset_max_version(12, "Squeeze/Unsqueeze changed in opset 13") + @check_opset_max_version(12, "Squeeze/Unsqueeze changed since opset 13") def test_transpose_with_squeeze3(self, input_shape, output_shape, perm): # squeeze the last dim node1 = helper.make_node("Transpose", ["X"], ["Y"], perm=perm, name="trans") @@ -857,7 +882,7 @@ def test_transpose_with_squeeze3(self, input_shape, output_shape, perm): ((3, 1, 4, 5), (3, 4, 5), [0, 2, 3, 1]), ((3, 1, 4, 5, 6), (3, 4, 5, 6), [0, 2, 3, 4, 1]), ]) - @check_opset_min_version(13, "Squeeze/Unsqueeze changed in opset 13") + @check_opset_min_version(13, "Squeeze/Unsqueeze changed since opset 13") def test_transpose_with_squeeze3_13(self, input_shape, output_shape, perm): # squeeze the last dim node1 = helper.make_node("Transpose", ["X"], ["Y"], perm=perm, name="trans") @@ -879,7 +904,7 @@ def test_transpose_with_squeeze3_13(self, input_shape, output_shape, perm): ((3, 1, 1, 5), (3, 5), [0, 2, 3, 1]), ((3, 1, 1, 5, 4), (3, 5, 4), [0, 2, 3, 4, 1]), ]) - @check_opset_max_version(12, "Squeeze/Unsqueeze changed in opset 13") + @check_opset_max_version(12, "Squeeze/Unsqueeze changed since opset 13") def test_transpose_with_squeeze4(self, input_shape, output_shape, perm): # squeeze the two dims node1 = helper.make_node("Transpose", ["X"], ["Y"], perm=perm, name="trans") @@ -900,7 +925,7 @@ def test_transpose_with_squeeze4(self, input_shape, output_shape, perm): ((3, 1, 1, 5), (3, 5), [0, 2, 3, 1]), ((3, 1, 1, 5, 4), (3, 5, 4), [0, 2, 3, 4, 1]), ]) - @check_opset_min_version(13, "Squeeze/Unsqueeze changed in opset 13") + @check_opset_min_version(13, "Squeeze/Unsqueeze changed since opset 13") def test_transpose_with_squeeze4_13(self, input_shape, output_shape, perm): # squeeze the two dims node1 = helper.make_node("Transpose", ["X"], ["Y"], perm=perm, name="trans") @@ -2156,7 +2181,7 @@ def test_const_fold_concat(self): self.run_and_compare(["res"], {"inp": np.random.randn(6, 12).astype(np.float32)}, model_proto, "Concat", 0) - @check_opset_max_version(12, "Squeeze/Unsqueeze changed in opset 13") + @check_opset_max_version(12, "Squeeze/Unsqueeze changed since opset 13") def test_const_fold_unsqueeze_with_const(self): shape = (6, 6) const_tensor = helper.make_tensor(name='const_tensor', data_type=TensorProto.FLOAT, dims=shape, @@ -2176,7 +2201,7 @@ def test_const_fold_unsqueeze_with_const(self): self.run_and_compare(["res"], {"X": np.random.randn(1).astype(np.float32)}, model_proto, "Unsqueeze", 0) - @check_opset_min_version(13, "Squeeze/Unsqueeze changed in opset 13") + @check_opset_min_version(13, "Squeeze/Unsqueeze changed since opset 13") def test_const_fold_unsqueeze_with_const_13(self): shape = (6, 6) const_tensor = helper.make_tensor(name='const_tensor', data_type=TensorProto.FLOAT, dims=shape, @@ -2254,7 +2279,7 @@ def test_const_fold_split_one(self): self.run_and_compare(["out4"], {"inp": np.random.randn(2, 6, 1).astype(np.float32)}, model_proto, "Split", 0) - @check_opset_min_version(13, "Split changed in opset 13") + @check_opset_min_version(13, "Split changed since opset 13") def test_const_fold_split_const_splits_13(self): shape = (2, 6, 1) const_tensor = helper.make_tensor(name='const_tensor', data_type=TensorProto.FLOAT, dims=shape, @@ -2277,7 +2302,7 @@ def test_const_fold_split_const_splits_13(self): self.run_and_compare(["out4"], {"inp": np.random.randn(2, 3, 1).astype(np.float32)}, model_proto, "Split", 0) - @check_opset_max_version(12, "Split changed in opset 13") + @check_opset_max_version(12, "Split changed since opset 13") def test_const_fold_split_const_splits(self): shape = (2, 6, 1) const_tensor = helper.make_tensor(name='const_tensor', data_type=TensorProto.FLOAT, dims=shape, diff --git a/tf2onnx/optimizer/transpose_optimizer.py b/tf2onnx/optimizer/transpose_optimizer.py index dd5377f0a..1a82a11fa 100644 --- a/tf2onnx/optimizer/transpose_optimizer.py +++ b/tf2onnx/optimizer/transpose_optimizer.py @@ -671,11 +671,19 @@ def _concat_handler(self, trans, node): def _split_handler(self, trans, node): # Todo: need handle cases where Split node has more than 1 outputs. + split = None + if self._g.opset >= 13 and len(node.input) > 1 and node.inputs[1].is_const(): + # split is an input not attr since opset 13 + split = node.inputs[1].get_tensor_value(as_list=True) if self._handle_node_having_branches(trans, node): perm = trans.get_attr_value("perm") axis = node.get_attr_value("axis", 0) new_axis = perm[axis] node.set_attr("axis", new_axis) + if split: + new_axes_np = np.array(split, dtype=np.int64) + new_axes_const = self._g.make_const(utils.make_name(node.inputs[1].name), new_axes_np) + self._g.replace_inputs(node, [node.input[0], new_axes_const.output[0]]) return True return False @@ -747,7 +755,7 @@ def _calculate_new_attr(ori_perm, ori_squeeze_axes): shape_after_trans = [input_shape[i] for i in ori_perm] output_shape = [shape_after_trans[i] for i in range(n) if i not in ori_squeeze_axes] # calculate new_perm - # after switch, the output shape should be same, using this condtion we can figure the new perm + # after switch, the output shape should be same, using this condition we can figure the new perm shape_after_squeeze = [input_shape[i] for i in range(n) if i not in new_squeeze_axes] new_perm = [shape_after_squeeze.index(i) for i in output_shape] @@ -757,7 +765,7 @@ def _calculate_new_attr(ori_perm, ori_squeeze_axes): return False axes = None - # in opset 13, axes is an input not attr + # axes is an input not attr since opset 13 if node.get_attr("axes"): axes = node.get_attr("axes").ints if len(node.input) > 1 and node.inputs[1].is_const(): From e7f39ed77ee19a17256cb874d18275dbb548ab33 Mon Sep 17 00:00:00 2001 From: q-ycong-p <50014064+q-ycong-p@users.noreply.github.com> Date: Wed, 20 Jul 2022 20:08:17 -0700 Subject: [PATCH 27/40] Skip existing const initializer node as input in _parse_graph_input (#2000) If the Graph object being constructed already contains a node of the name of a Const node in orginal graph, do not add as input. Signed-off-by: Yu Cong Co-authored-by: Yu Cong Co-authored-by: Deyu Huang --- tests/test_optimizers.py | 2 +- tf2onnx/graph.py | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/test_optimizers.py b/tests/test_optimizers.py index 594c625e2..ab62b4a20 100644 --- a/tests/test_optimizers.py +++ b/tests/test_optimizers.py @@ -1927,7 +1927,7 @@ def test_duplicated_duplicated_constant_and_initializer(self): model_proto = self.make_model(graph, producer_name="onnx-tests") self.run_merge_duplicated_nodes_compare(["OUT"], {}, model_proto, op_type="Constant", remaining_op_num=0, - graph_validator=lambda g: self._check_initializer_num(g, 2)) + graph_validator=lambda g: self._check_initializer_num(g, 1)) def test_duplicated_node_is_graph_output(self): node0 = helper.make_node('Add', inputs=["X", "X"], outputs=["value0"]) diff --git a/tf2onnx/graph.py b/tf2onnx/graph.py index e98eea9db..82c93c695 100644 --- a/tf2onnx/graph.py +++ b/tf2onnx/graph.py @@ -1791,9 +1791,11 @@ def _parse_graph_input(g, graph_proto, const_node_names): # because for subgraphs, the input orders matter. for graph_input in graph_proto.input: name = graph_input.name - shape = shapes[name] - dtype = dtypes[name] - if name not in const_node_names: - g.add_graph_input(name, dtype, shape) - else: - g.add_graph_input_with_default(name, g.get_node_by_name(name), dtype, shape) + const_initializer_node = g.get_node_by_output_in_current_graph(name) + if const_initializer_node is None: # is actual input rather than initializer + shape = shapes[name] + dtype = dtypes[name] + if name not in const_node_names: + g.add_graph_input(name, dtype, shape) + else: + g.add_graph_input_with_default(name, g.get_node_by_name(name), dtype, shape) From 71105c1d33b48ef35d15a9dd6b33c7b8b29ea3f7 Mon Sep 17 00:00:00 2001 From: q-ycong-p <50014064+q-ycong-p@users.noreply.github.com> Date: Thu, 21 Jul 2022 19:45:57 -0700 Subject: [PATCH 28/40] Add handling of HardSigmoid recurrent activation for Keras LSTM (#2001) Add pattern matching and parsing where Keras LSTM uses `HardSigmoid` as the recurrent activation. Signed-off-by: Yu Cong --- tests/test_lstm.py | 22 ++++++++++++ tf2onnx/rewriter/lstm_tf2_rewriter.py | 51 ++++++++++++++++++-------- tf2onnx/rewriter/rnn_utils.py | 52 ++++++++++++++++++--------- 3 files changed, 94 insertions(+), 31 deletions(-) diff --git a/tests/test_lstm.py b/tests/test_lstm.py index 736935285..a79829e87 100644 --- a/tests/test_lstm.py +++ b/tests/test_lstm.py @@ -751,6 +751,28 @@ def func(x): return tf.identity(y[0], name="output"), tf.identity(y[1], name="output1") self.run_test_case(func, {"input:0": x_val}, [], ["output:0", "output1:0"], rtol=1e-05, atol=1e-06) + @check_tf_min_version("2.0") + @skip_tf_versions("2.1", "Bug in TF 2.1") + def test_keras_lstm_recurrent_activation_is_hard_sigmoid(self): + in_shape = [10, 3] + x_val = np.random.uniform(size=[2, 10, 3]).astype(np.float32) + + model_in = tf.keras.layers.Input(tuple(in_shape), batch_size=2) + x = tf.keras.layers.LSTM( + units=5, + return_sequences=True, + return_state=True, + kernel_initializer=tf.random_uniform_initializer(0.0, 1.0, seed=42), + recurrent_initializer=tf.random_uniform_initializer(0.0, 1.0, seed=44), + bias_initializer=tf.random_uniform_initializer(0.0, 1.0, seed=43), + recurrent_activation="hard_sigmoid" + )(model_in) + model = tf.keras.models.Model(inputs=model_in, outputs=x) + + def func(x): + y = model(x) + return tf.identity(y[0], name="output"), tf.identity(y[1], name="output1") + self.run_test_case(func, {"input:0": x_val}, [], ["output:0", "output1:0"], rtol=1e-05, atol=1e-06) if __name__ == '__main__': unittest_main() diff --git a/tf2onnx/rewriter/lstm_tf2_rewriter.py b/tf2onnx/rewriter/lstm_tf2_rewriter.py index 414bf6c98..845bb2a84 100644 --- a/tf2onnx/rewriter/lstm_tf2_rewriter.py +++ b/tf2onnx/rewriter/lstm_tf2_rewriter.py @@ -16,29 +16,52 @@ # pylint: disable=invalid-name,unused-argument,missing-docstring, unused-variable +def _make_lstm_pattern_from_params(params): + return make_lstm_pattern(enter_or_id="Identity") if not params.get("from_keras", False) \ + else make_lstm_pattern( + from_keras=True, + use_bias=params.get("use_bias", False), + activation=params.get("activation", ""), + recurrent_activation=params.get("recurrent_activation", "") + ) def rewriter_lstm_tf2(g, ops): - - pattern1 = make_lstm_pattern(enter_or_id="Identity") # TF LSTM - pattern2 = make_lstm_pattern(from_keras=True, use_bias=False) # keras LSTM - pattern3 = make_lstm_pattern(from_keras=True, use_bias=True) # keras LSTM with bias - - for pattern in [pattern1, pattern2, pattern3]: + lstm_params_variations = [ + # default activations + {"enter_or_id": "Identity"}, # TF LSTM + {"from_keras": True, "use_bias": False}, # keras LSTM + {"from_keras": True, "use_bias": True}, # keras LSTM with bias + # hard sigmoid as recurrent activation + {"from_keras": True, "use_bias": False, "recurrent_activation": "hard_sigmoid"}, # keras LSTM + {"from_keras": True, "use_bias": True, "recurrent_activation": "hard_sigmoid"} # keras LSTM with bias + # Note: add other LSTM variations as needed + ] + for params in lstm_params_variations: + pattern = _make_lstm_pattern_from_params(params) matcher = GraphMatcher(pattern, allow_reorder=False) match_results = list(matcher.match_ops(ops)) for match_result in match_results: - from_keras = pattern != pattern1 + is_ft_hard_sigmoid = params.get("recurrent_activation", "") == "hard_sigmoid" + recurrent_activation_f = "HardSigmoid" if is_ft_hard_sigmoid else \ + match_result.get_op("ft").type + activation_g = match_result.get_op("gt").type + activation_h = match_result.get_op("ct'").type + + default_activations = ["Relu", "Sigmoid", "Tanh"] + if ((activation_g not in default_activations) or + (activation_h not in default_activations) or + (not is_ft_hard_sigmoid and recurrent_activation_f not in default_activations)): + continue + activations_fgh = [ - match_result.get_op("ft").type, - match_result.get_op("gt").type, - match_result.get_op("ct'").type + recurrent_activation_f, + activation_g, + activation_h ] - supported_activations = ['Relu', 'Sigmoid', 'Tanh'] - if any(f not in supported_activations for f in activations_fgh): - continue # extract input x_t + from_keras = params.get("from_keras", False) if from_keras: get_item = match_result.get_op("xt") else: @@ -134,7 +157,7 @@ def has_tensor_list_consumer(n): # Wb and Rb are concatenated b_idx = None - if pattern is pattern3: + if from_keras and params.get("use_bias", False): bias_add = match_result.get_op("bias_add") if bias_add is not None and bias_add.data_format != "NHWC": continue diff --git a/tf2onnx/rewriter/rnn_utils.py b/tf2onnx/rewriter/rnn_utils.py index 4e3912004..b94ef3ffb 100644 --- a/tf2onnx/rewriter/rnn_utils.py +++ b/tf2onnx/rewriter/rnn_utils.py @@ -30,6 +30,25 @@ class REWRITER_RESULT(Enum): # TensorFlow LSTMCell/BasicLSTMCell and Keras LSTM computation graph matching +def insert_activation(activation, name="", inputs=None): + inputs = inputs if inputs else [] # to avoid empty list as default arg + if activation == "hard_sigmoid": + return OpTypePattern("Maximum", inputs=[ + OpTypePattern("Minimum", inputs=[ + OpTypePattern("Add|AddV2", inputs=[ + OpTypePattern("Mul", inputs=[ + *inputs, + OpTypePattern("*") # mul(x, 0.2) + ]), OpTypePattern("*") # add(x, 0.5) + ]), OpTypePattern("*") # minimum(x, 1) + ]), OpTypePattern("*") # maximum(x, 0) + ]) + # Additional activation pattern can be added when needed: + # https://www.tensorflow.org/api_docs/python/tf/keras/activations + # otherwise, use default activations + return OpTypePattern("Tanh|Relu|Sigmoid", name=name, inputs=inputs) + + def make_lstm_xc_pattern(enter_or_id="Enter", from_keras=False, use_bias=False): if from_keras: lstm_xh_pattern = OpTypePattern("Add|AddV2", allow_reorder=False, inputs=[ @@ -63,7 +82,8 @@ def make_lstm_xc_pattern(enter_or_id="Enter", from_keras=False, use_bias=False): ]) -def make_lstm_pattern(enter_or_id="Enter", from_keras=False, use_bias=False): +def make_lstm_pattern(enter_or_id="Enter", from_keras=False, use_bias=False, + activation="", recurrent_activation=""): # split (Xt*(W[ifco]^T) + Ht-1*(R[ifco]^T)) on 'Const' axis lstm_xc_pattern = OpTypePattern('Split', inputs=[ OpTypePattern("Const"), @@ -77,23 +97,21 @@ def make_lstm_pattern(enter_or_id="Enter", from_keras=False, use_bias=False): OpTypePattern("*", name="ft_bias"), ]) - activation = "Tanh|Relu|Sigmoid" - recurrent_activation = "Tanh|Relu|Sigmoid" - - return OpTypePattern("Mul", name='ht', inputs=[ - OpTypePattern(recurrent_activation, name="ot", inputs=[lstm_xc_pattern]), - OpTypePattern(activation, name="ct'", inputs=[ - OpTypePattern("Add|AddV2", name="ct", inputs=[ - OpTypePattern("Mul", name="ct_identity_consumer", inputs=[ - OpTypePattern(recurrent_activation, name="ft", inputs=[lstm_fb_pattern]), - OpTypePattern("*", name="c"), - ]), - OpTypePattern("Mul", inputs=[ - OpTypePattern(recurrent_activation, name="it", inputs=[lstm_xc_pattern]), - OpTypePattern(activation, name="gt", inputs=[lstm_xc_pattern]), - ]), - ]), + # cell state + lstm_ct_pattern = OpTypePattern("Add|AddV2", name="ct", inputs=[ + OpTypePattern("Mul", name="ct_identity_consumer", inputs=[ + insert_activation(recurrent_activation, name="ft", inputs=[lstm_fb_pattern]), + OpTypePattern("*", name="c"), ]), + OpTypePattern("Mul", inputs=[ + insert_activation(recurrent_activation, name="it", inputs=[lstm_xc_pattern]), + insert_activation(activation, name="gt", inputs=[lstm_xc_pattern]), + ]), + ]) + + return OpTypePattern("Mul", name="ht", inputs=[ + insert_activation(recurrent_activation, name="ot", inputs=[lstm_xc_pattern]), + insert_activation(activation, name="ct'", inputs=[lstm_ct_pattern]), ]) lstmcell_pattern = make_lstm_pattern() From 404e2b7f7c7981db78b2c3dac8d4bb2d09887881 Mon Sep 17 00:00:00 2001 From: Jay Zhang <36183870+fatcat-z@users.noreply.github.com> Date: Wed, 27 Jul 2022 00:41:40 +0800 Subject: [PATCH 29/40] Add more tests for tf 2.9.x into CI pipelines. (#2009) * Add more tests to cover tf 2.9.x. * Set tensorflowjs to a fixed version 3.18.0. * Fix an issue of installing tensorflow-text component. Signed-off-by: Jay Zhang --- .../onnxruntime_nightly_test.yml | 18 +++++++++-- .../pretrained_model_test-matrix.yml | 11 ++++++- .../azure_pipelines/pretrained_model_test.yml | 4 +-- ci_build/azure_pipelines/templates/setup.yml | 4 ++- .../trimmed_keras2onnx_application_tests.yml | 2 +- ci_build/azure_pipelines/unit_test-matrix.yml | 22 ++++++------- ci_build/azure_pipelines/unit_test.yml | 32 +++++++++---------- 7 files changed, 58 insertions(+), 35 deletions(-) diff --git a/ci_build/azure_pipelines/onnxruntime_nightly_test.yml b/ci_build/azure_pipelines/onnxruntime_nightly_test.yml index 8d75e5138..41e8f32de 100644 --- a/ci_build/azure_pipelines/onnxruntime_nightly_test.yml +++ b/ci_build/azure_pipelines/onnxruntime_nightly_test.yml @@ -20,7 +20,7 @@ stages: parameters: platforms: ['linux', 'windows'] python_versions: ['3.7'] - tf_versions: ['1.13.1', '1.14.0'] + tf_versions: ['1.14.0'] onnx_opsets: [''] onnx_backends: {onnxruntime: ['nightly']} job: @@ -43,8 +43,8 @@ stages: - template: 'templates/job_generator.yml' parameters: platforms: ['linux'] - python_versions: ['3.7'] - tf_versions: ['2.4.1'] + python_versions: ['3.9'] + tf_versions: ['2.9.1'] onnx_opsets: [''] onnx_backends: {onnxruntime: ['nightly']} job: @@ -64,6 +64,18 @@ stages: - template: 'unit_test.yml' report_coverage: 'True' + - template: 'templates/job_generator.yml' + parameters: + platforms: ['linux'] + python_versions: ['3.8'] + tf_versions: ['2.7.3'] + onnx_opsets: [''] + onnx_backends: {onnxruntime: ['nightly']} + job: + steps: + - template: 'unit_test.yml' + report_coverage: 'True' + - template: 'templates/job_generator.yml' parameters: platforms: ['linux'] diff --git a/ci_build/azure_pipelines/pretrained_model_test-matrix.yml b/ci_build/azure_pipelines/pretrained_model_test-matrix.yml index e0fd83d0e..e726435f4 100755 --- a/ci_build/azure_pipelines/pretrained_model_test-matrix.yml +++ b/ci_build/azure_pipelines/pretrained_model_test-matrix.yml @@ -5,7 +5,7 @@ jobs: parameters: platforms: ['linux', 'windows'] python_versions: ['3.7'] - tf_versions: ['1.13.1', '1.14.0'] + tf_versions: ['1.14.0'] job: steps: - template: 'pretrained_model_test.yml' @@ -46,6 +46,15 @@ jobs: steps: - template: 'pretrained_model_test.yml' +- template: 'templates/job_generator.yml' + parameters: + platforms: ['linux', 'windows'] + python_versions: ['3.9'] + tf_versions: ['2.9.1'] + job: + steps: + - template: 'pretrained_model_test.yml' + schedules: - cron: "0 10 * * *" displayName: pre-trained model test, full matrix diff --git a/ci_build/azure_pipelines/pretrained_model_test.yml b/ci_build/azure_pipelines/pretrained_model_test.yml index 0fe9900f5..21772e02b 100644 --- a/ci_build/azure_pipelines/pretrained_model_test.yml +++ b/ci_build/azure_pipelines/pretrained_model_test.yml @@ -17,7 +17,7 @@ jobs: parameters: # 2.7, tf python_versions: ['3.7'] - tf_versions: ['1.15.5','2.7.0'] + tf_versions: ['1.15.5','2.8.0'] job: steps: - template: 'pretrained_model_test.yml' @@ -26,7 +26,7 @@ jobs: parameters: # 2.8, tf python_versions: ['3.9'] - tf_versions: ['2.8.0'] + tf_versions: ['2.9.1'] job: steps: - template: 'pretrained_model_test.yml' diff --git a/ci_build/azure_pipelines/templates/setup.yml b/ci_build/azure_pipelines/templates/setup.yml index cc6d4bc3d..14a73247b 100644 --- a/ci_build/azure_pipelines/templates/setup.yml +++ b/ci_build/azure_pipelines/templates/setup.yml @@ -34,7 +34,7 @@ steps: if [[ $CI_SKIP_TFJS_TESTS == "False" ]] ; then - pip install tensorflowjs + pip install tensorflowjs==3.18.0 npm install @tensorflow/tfjs fi @@ -64,6 +64,8 @@ steps: if [[ $CI_TF_VERSION == 2.8* ]] ; then pip install "tensorflow-text>=2.8,<2.9" + else + pip install tensorflow-text fi fi diff --git a/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml b/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml index edf1c2f55..5191c6630 100644 --- a/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml +++ b/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml @@ -25,7 +25,7 @@ jobs: ONNX_PATH: onnx==1.11.0 INSTALL_KERAS: UNINSTALL_KERAS: - INSTALL_TENSORFLOW: pip install tensorflow==2.9.0 + INSTALL_TENSORFLOW: pip install tensorflow==2.9.1 INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_KERAS_RESNET: pip install keras-resnet INSTALL_TRANSFORMERS: pip install transformers==3.4.0 diff --git a/ci_build/azure_pipelines/unit_test-matrix.yml b/ci_build/azure_pipelines/unit_test-matrix.yml index 3d05e319b..4053c3729 100644 --- a/ci_build/azure_pipelines/unit_test-matrix.yml +++ b/ci_build/azure_pipelines/unit_test-matrix.yml @@ -3,17 +3,6 @@ stages: - stage: jobs: - - template: 'templates/job_generator.yml' - parameters: - platforms: ['linux', 'windows'] - python_versions: ['3.7'] - tf_versions: ['1.13.1'] - onnx_opsets: [''] - job: - steps: - - template: 'unit_test.yml' - report_coverage: 'True' - - template: 'templates/job_generator.yml' parameters: platforms: ['linux', 'windows'] @@ -69,4 +58,15 @@ stages: - template: 'unit_test.yml' report_coverage: 'True' + - template: 'templates/job_generator.yml' + parameters: + platforms: ['linux', 'windows'] + python_versions: ['3.9'] + tf_versions: ['2.9.0'] + onnx_opsets: [''] + job: + steps: + - template: 'unit_test.yml' + report_coverage: 'True' + - template: 'templates/combine_test_coverage.yml' diff --git a/ci_build/azure_pipelines/unit_test.yml b/ci_build/azure_pipelines/unit_test.yml index cbcc39e37..fa68a1d20 100644 --- a/ci_build/azure_pipelines/unit_test.yml +++ b/ci_build/azure_pipelines/unit_test.yml @@ -79,9 +79,9 @@ stages: - template: 'templates/job_generator.yml' parameters: - # TFJS tf 2.6 + # TFJS tf 2.9 python_versions: ['3.9'] - tf_versions: ['2.6.2'] + tf_versions: ['2.9.1'] onnx_opsets: [''] skip_tfjs_tests: 'False' skip_tf_tests: 'True' @@ -92,9 +92,9 @@ stages: - template: 'templates/job_generator.yml' parameters: - # TFLite tf 2.6 + # TFLite tf 2.9 python_versions: ['3.8'] - tf_versions: ['2.6.2'] + tf_versions: ['2.9.1'] onnx_opsets: [''] skip_tflite_tests: 'False' skip_tf_tests: 'True' @@ -105,9 +105,9 @@ stages: - template: 'templates/job_generator.yml' parameters: - # tf 2.6 + # tf 2.9 python_versions: ['3.8'] - tf_versions: ['2.6.2'] + tf_versions: ['2.9.1'] onnx_opsets: [''] job: steps: @@ -127,10 +127,9 @@ stages: - template: 'templates/job_generator.yml' parameters: - # tf 1.13 - python_versions: [3.7'] - tf_versions: ['1.13.1'] - onnx_opsets: ['9'] + platforms: ['windows'] + tf_versions: ['1.14.0'] + onnx_opsets: ['14'] job: steps: - template: 'unit_test.yml' @@ -138,9 +137,10 @@ stages: - template: 'templates/job_generator.yml' parameters: + python_versions: ['3.7'] platforms: ['windows'] - tf_versions: ['1.14.0'] - onnx_opsets: ['14'] + tf_versions: ['2.4.1'] + onnx_opsets: ['13'] job: steps: - template: 'unit_test.yml' @@ -148,10 +148,10 @@ stages: - template: 'templates/job_generator.yml' parameters: - python_versions: ['3.7'] + python_versions: ['3.8'] platforms: ['windows'] - tf_versions: ['2.4.1'] - onnx_opsets: ['13'] + tf_versions: ['2.8.1'] + onnx_opsets: ['15'] job: steps: - template: 'unit_test.yml' @@ -161,7 +161,7 @@ stages: parameters: python_versions: ['3.9'] platforms: ['windows'] - tf_versions: ['2.8.1'] + tf_versions: ['2.9.1'] onnx_opsets: ['15'] job: steps: From 1c7d4cefdb82cb904a48f21c593fcb310459ec75 Mon Sep 17 00:00:00 2001 From: southfreebird Date: Wed, 27 Jul 2022 03:00:50 +0300 Subject: [PATCH 30/40] Fix problem with adding more than one tf.newaxis at the same time (#2007) Signed-off-by: southfreebird Co-authored-by: iolkhovsky --- tests/test_backend.py | 18 ++++++++++++++++++ tf2onnx/onnx_opset/tensor.py | 23 +++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index fe50e0591..4e4d08969 100755 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -5893,5 +5893,23 @@ def func(x): x_val = make_xval([3, 4]) self._run_test_case(func, [_OUTPUT], {_INPUT: x_val}) + @check_opset_min_version(10, "Slice") + def test_addition_two_newaxis_simultaneously(self): + def func(x): + op = x[..., tf.newaxis, tf.newaxis] + return tf.identity(op, name=_TFOUTPUT) + + x_val = make_xval([2, 3]) + self._run_test_case(func, [_OUTPUT], {_INPUT: x_val}) + + @check_opset_min_version(10, "Slice") + def test_addition_three_newaxis_simultaneously(self): + def func(x): + op = x[..., tf.newaxis, tf.newaxis, tf.newaxis] + return tf.identity(op, name=_TFOUTPUT) + + x_val = make_xval([2, 3]) + self._run_test_case(func, [_OUTPUT], {_INPUT: x_val}) + if __name__ == '__main__': unittest_main() diff --git a/tf2onnx/onnx_opset/tensor.py b/tf2onnx/onnx_opset/tensor.py index 9ad2b513e..b08188b88 100644 --- a/tf2onnx/onnx_opset/tensor.py +++ b/tf2onnx/onnx_opset/tensor.py @@ -974,6 +974,29 @@ def any_version_after10(cls, opset, ctx, node, **kwargs): begin_mask |= 1 << bit end_mask |= 1 << bit + if ellipsis_mask: + unqueeze_at = [] + ellipsis_gap = 0 + num_new = 0 + end_mask = node.get_attr("end_mask") + end_mask = end_mask.i if end_mask is not None else 0 + begin_mask = node.get_attr("begin_mask") + begin_mask = begin_mask.i if begin_mask is not None else 0 + + for bit in range(32): + new_axis_flag = (new_axis_mask >> bit) & 1 + ellipsis_flag = (ellipsis_mask >> bit) & 1 + num_new += not ellipsis_flag and new_axis_flag + + for bit in range(32): + if (ellipsis_mask >> bit) & 1: + ellipsis_gap = len(ctx.get_shape(input_x)) - param_rank + num_new + 1 + elif (new_axis_mask >> bit) & 1: + effective_bit = bit if not ellipsis_gap else bit + ellipsis_gap - 1 + unqueeze_at.append(effective_bit) + begin_mask |= 1 << bit + end_mask |= 1 << bit + input_x = GraphBuilder(ctx).make_unsqueeze( {'data': input_x, 'axes': unqueeze_at}) From d72b4d11b3bfd16a4ddc9c1ce0d0822855913b3b Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Thu, 28 Jul 2022 07:19:18 +0800 Subject: [PATCH 31/40] Improve ZerosLike implementation and optimize for opset >= 9 (#2003) * Improve ZerosLike implementation for opset >= 9 Signed-off-by: Deyu Huang Co-authored-by: Guenther Schmuelling * add a blank line Signed-off-by: Deyu Huang Co-authored-by: Guenther Schmuelling Co-authored-by: Jay Zhang <36183870+fatcat-z@users.noreply.github.com> --- tests/test_backend.py | 13 +++++ tests/test_optimizers.py | 66 +++++++++++++++++++++++ tf2onnx/onnx_opset/generator.py | 13 ++++- tf2onnx/optimizer/const_fold_optimizer.py | 24 +++++++++ 4 files changed, 115 insertions(+), 1 deletion(-) diff --git a/tests/test_backend.py b/tests/test_backend.py index 4e4d08969..a26f4fdba 100755 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -3887,6 +3887,19 @@ def func(x, y): self._run_test_case(func, [_OUTPUT], {_INPUT: input_x > 0.5, _INPUT1: input_y}) + @check_opset_min_version(9, "ConstantOfShape") + def test_zeros_like_opset9(self): + input_x = np.random.random_sample([3, 16, 16]).astype(np.float32) + input_y = np.array([16, 16, 3]).astype(np.int64) + + def func(x, y): + z = tf.reshape(x, y) + return tf.zeros_like(z, name=_TFOUTPUT) + + self._run_test_case(func, [_OUTPUT], {_INPUT: input_x, _INPUT1: input_y}) + self._run_test_case(func, [_OUTPUT], {_INPUT: input_x.astype(np.int32), _INPUT1: input_y}, as_session=True, + graph_validator=lambda g: check_op_count(g, "ConstantOfShape", 1, disabled=False)) + @check_opset_min_version(9, "is_nan") def test_isnan(self): # only compatible with dtype `float32` diff --git a/tests/test_optimizers.py b/tests/test_optimizers.py index ab62b4a20..8261d083f 100644 --- a/tests/test_optimizers.py +++ b/tests/test_optimizers.py @@ -2241,6 +2241,72 @@ def test_const_fold_cast_with_const(self): self.run_and_compare(["res"], {"X": np.random.randn(*shape).astype(np.int64)}, model_proto, "Cast", 0) + def test_const_fold_add(self): + shape = (6, 6) + const_tensor1 = helper.make_tensor(name='const_tensor', data_type=TensorProto.FLOAT, dims=shape, + vals=np.random.randn(*shape).flatten().astype(np.float32)) + const_tensor2 = helper.make_tensor(name='const_tensor', data_type=TensorProto.FLOAT, dims=shape, + vals=np.random.randn(*shape).flatten().astype(np.float32)) + node1 = helper.make_node("Constant", [], ["const1"], value=const_tensor1) + node2 = helper.make_node("Constant", [], ["const2"], value=const_tensor2) + node3 = helper.make_node("Add", ["const1", "const2"], ["add"]) + node4 = helper.make_node("Add", ["add", "X"], ["res"]) + + graph = helper.make_graph( + [node1, node2, node3, node4], + "test_const_fold_add", + [helper.make_tensor_value_info("X", TensorProto.FLOAT, shape)], + [helper.make_tensor_value_info("res", TensorProto.FLOAT, shape)], + ) + + model_proto = self.make_model(graph, producer_name="onnx-tests") + self.run_and_compare(["res"], {"X": np.random.randn(*shape).astype(np.float32)}, model_proto, + "Add", 1) + + def test_const_fold_sub(self): + shape = (6, 6) + const_tensor1 = helper.make_tensor(name='const_tensor', data_type=TensorProto.FLOAT, dims=shape, + vals=np.random.randn(*shape).flatten().astype(np.float32)) + const_tensor2 = helper.make_tensor(name='const_tensor', data_type=TensorProto.FLOAT, dims=shape, + vals=np.random.randn(*shape).flatten().astype(np.float32)) + node1 = helper.make_node("Constant", [], ["const1"], value=const_tensor1) + node2 = helper.make_node("Constant", [], ["const2"], value=const_tensor2) + node3 = helper.make_node("Sub", ["const1", "const2"], ["sub"]) + node4 = helper.make_node("Sub", ["sub", "X"], ["res"]) + + graph = helper.make_graph( + [node1, node2, node3, node4], + "test_const_fold_sub", + [helper.make_tensor_value_info("X", TensorProto.FLOAT, shape)], + [helper.make_tensor_value_info("res", TensorProto.FLOAT, shape)], + ) + + model_proto = self.make_model(graph, producer_name="onnx-tests") + self.run_and_compare(["res"], {"X": np.random.randn(*shape).astype(np.float32)}, model_proto, + "Sub", 1) + + def test_const_fold_mul(self): + shape = (6, 6) + const_tensor1 = helper.make_tensor(name='const_tensor', data_type=TensorProto.FLOAT, dims=shape, + vals=np.random.randn(*shape).flatten().astype(np.float32)) + const_tensor2 = helper.make_tensor(name='const_tensor', data_type=TensorProto.FLOAT, dims=shape, + vals=np.random.randn(*shape).flatten().astype(np.float32)) + node1 = helper.make_node("Constant", [], ["const1"], value=const_tensor1) + node2 = helper.make_node("Constant", [], ["const2"], value=const_tensor2) + node3 = helper.make_node("Mul", ["const1", "const2"], ["mul"]) + node4 = helper.make_node("Mul", ["mul", "X"], ["res"]) + + graph = helper.make_graph( + [node1, node2, node3, node4], + "test_const_fold_mul", + [helper.make_tensor_value_info("X", TensorProto.FLOAT, shape)], + [helper.make_tensor_value_info("res", TensorProto.FLOAT, shape)], + ) + + model_proto = self.make_model(graph, producer_name="onnx-tests") + self.run_and_compare(["res"], {"X": np.random.randn(*shape).astype(np.float32)}, model_proto, + "Mul", 1) + def test_const_fold_split(self): shape = (2, 6, 1) const_tensor = helper.make_tensor(name='const_tensor', data_type=TensorProto.FLOAT, dims=shape, diff --git a/tf2onnx/onnx_opset/generator.py b/tf2onnx/onnx_opset/generator.py index 90eb1b62c..0b59dca6b 100644 --- a/tf2onnx/onnx_opset/generator.py +++ b/tf2onnx/onnx_opset/generator.py @@ -8,7 +8,7 @@ import logging import numpy as np -from onnx import onnx_pb, numpy_helper +from onnx import onnx_pb, numpy_helper, helper from tf2onnx import utils from tf2onnx.handler import tf_op from tf2onnx.graph_builder import GraphBuilder @@ -242,6 +242,17 @@ def version_1(cls, ctx, node, **kwargs): name=node.name, outputs=node.output, shapes=shapes, dtypes=dtypes) + @classmethod + def version_9(cls, ctx, node, **kwargs): + dtypes = node.output_dtypes + ctx.remove_node(node.name) + shape = ctx.make_node("Shape", node.input).output[0] + zero_tensor = helper.make_tensor("value", dtypes[0], [1], vals=[0]) + ctx.make_node("ConstantOfShape", inputs=[shape], + attr={'value': zero_tensor}, + name=node.name, outputs=node.output, + dtypes=dtypes) + @tf_op(["IteratorV2", "FIFOQueueV2"]) class Iterator: diff --git a/tf2onnx/optimizer/const_fold_optimizer.py b/tf2onnx/optimizer/const_fold_optimizer.py index cc806f4a0..81f479ca3 100644 --- a/tf2onnx/optimizer/const_fold_optimizer.py +++ b/tf2onnx/optimizer/const_fold_optimizer.py @@ -162,6 +162,30 @@ def _fold_unsqueeze(node, graph): const_val_after_unsqueeze = const_val.reshape(shape_out) return [const_val_after_unsqueeze] + @staticmethod + @_register_func("Mul") + def _fold_mul(node, graph): + const_val1 = node.inputs[0].get_tensor_value(as_list=False) + const_val2 = node.inputs[1].get_tensor_value(as_list=False) + const_val_after_nul = np.multiply(const_val1, const_val2) + return [const_val_after_nul] + + @staticmethod + @_register_func("Add") + def _fold_add(node, graph): + const_val1 = node.inputs[0].get_tensor_value(as_list=False) + const_val2 = node.inputs[1].get_tensor_value(as_list=False) + const_val_after_add = np.add(const_val1, const_val2) + return [const_val_after_add] + + @staticmethod + @_register_func("Sub") + def _fold_sub(node, graph): + const_val1 = node.inputs[0].get_tensor_value(as_list=False) + const_val2 = node.inputs[1].get_tensor_value(as_list=False) + const_val_after_sub = np.subtract(const_val1, const_val2) + return [const_val_after_sub] + @staticmethod @_register_func("Split") def _fold_split(node, graph): From f30f41fbd069413f237da536150cbb377c5af74f Mon Sep 17 00:00:00 2001 From: Jay Zhang <36183870+fatcat-z@users.noreply.github.com> Date: Thu, 28 Jul 2022 14:31:24 +0800 Subject: [PATCH 32/40] Add newly required dependencies for latest ORT version. (#2012) Signed-off-by: Jay Zhang Co-authored-by: Deyu Huang --- ci_build/azure_pipelines/templates/setup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci_build/azure_pipelines/templates/setup.yml b/ci_build/azure_pipelines/templates/setup.yml index 14a73247b..6bb950643 100644 --- a/ci_build/azure_pipelines/templates/setup.yml +++ b/ci_build/azure_pipelines/templates/setup.yml @@ -3,7 +3,7 @@ steps: - bash: | set -ex - pip install pytest pytest-cov pytest-runner coverage graphviz requests pyyaml pillow pandas parameterized + pip install pytest pytest-cov pytest-runner coverage graphviz requests pyyaml pillow pandas parameterized sympy coloredlogs flatbuffers pip install $(CI_PIP_TF_NAME) $(CI_PIP_ONNX_NAME) # protobuf release version 4.21.0 has some Python changes which is not compatible with tensorflow so far. From 087045d4b61e231897f1232de59609d30013b8f5 Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Fri, 29 Jul 2022 16:34:24 +0800 Subject: [PATCH 33/40] Add support for Python310 and ORT 1.12 (#1975) * python3.10 support Signed-off-by: Deyu Huang * add tests except keras Signed-off-by: Deyu Huang * add protubuf limit for tf 2.9.1 and ort to 1.12.0 Signed-off-by: Deyu Huang * onnxruntime-extensions/yaml is not support with python3.10 Signed-off-by: Deyu Huang * remove onnxruntime-extensions support in py310 Signed-off-by: Deyu Huang Co-authored-by: Jay Zhang --- README.md | 6 +++--- .../azure_pipelines/pretrained_model_test.yml | 4 ++-- .../azure_pipelines/templates/job_generator.yml | 2 +- ci_build/azure_pipelines/templates/setup.yml | 17 ++++++++++++++--- ci_build/azure_pipelines/unit_test-matrix.yml | 2 +- ci_build/azure_pipelines/unit_test.yml | 13 ++++++++++++- setup.py | 3 ++- tests/test_backend.py | 2 ++ 8 files changed, 37 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index bd4753e6b..d6555e77d 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ The common issues we run into we try to document here [Troubleshooting Guide](Tr | Build Type | OS | Python | TensorFlow | ONNX opset | Status | | --- | --- | --- | --- | --- | --- | -| Unit Test - Basic | Linux, MacOS\*, Windows\* | 3.7-3.9 | 1.13-1.15, 2.1-2.8 | 9-16 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=16&branchName=main) | -| Unit Test - Full | Linux, MacOS, Windows | 3.7-3.9 | 1.13-1.15, 2.1-2.8 | 9-16 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test-matrix?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=18&branchName=main) | | +| Unit Test - Basic | Linux, MacOS\*, Windows\* | 3.7-3.10 | 1.13-1.15, 2.1-2.8 | 9-16 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=16&branchName=main) | +| Unit Test - Full | Linux, MacOS, Windows | 3.7-3.10 | 1.13-1.15, 2.1-2.8 | 9-16 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test-matrix?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=18&branchName=main) | |
## Supported Versions @@ -42,7 +42,7 @@ You can install tf2onnx on top of tf-1.x or tf-2.x. ### Python -We support Python ```3.7-3.9```. +We support Python ```3.7-3.10```. Note that on windows for Python > 3.7 the protobuf package doesn't use the cpp implementation and is very slow - we recommend to use Python 3.7 for that reason. ## Prerequisites diff --git a/ci_build/azure_pipelines/pretrained_model_test.yml b/ci_build/azure_pipelines/pretrained_model_test.yml index 21772e02b..6e9e6ed42 100644 --- a/ci_build/azure_pipelines/pretrained_model_test.yml +++ b/ci_build/azure_pipelines/pretrained_model_test.yml @@ -15,7 +15,7 @@ jobs: - template: 'templates/job_generator.yml' parameters: - # 2.7, tf + # 2.8, tf python_versions: ['3.7'] tf_versions: ['1.15.5','2.8.0'] job: @@ -24,7 +24,7 @@ jobs: - template: 'templates/job_generator.yml' parameters: - # 2.8, tf + # 2.9, tf python_versions: ['3.9'] tf_versions: ['2.9.1'] job: diff --git a/ci_build/azure_pipelines/templates/job_generator.yml b/ci_build/azure_pipelines/templates/job_generator.yml index 7859f5991..c0f23a30a 100644 --- a/ci_build/azure_pipelines/templates/job_generator.yml +++ b/ci_build/azure_pipelines/templates/job_generator.yml @@ -6,7 +6,7 @@ parameters: tf_versions: [''] onnx_versions: [''] onnx_opsets: ['16', '15', '14', '13', '12', '11', '10', '9'] - onnx_backends: {onnxruntime: ['1.11.0']} + onnx_backends: {onnxruntime: ['1.12.0']} job: {} run_setup: 'True' report_coverage: 'False' diff --git a/ci_build/azure_pipelines/templates/setup.yml b/ci_build/azure_pipelines/templates/setup.yml index 6bb950643..d5494e7b5 100644 --- a/ci_build/azure_pipelines/templates/setup.yml +++ b/ci_build/azure_pipelines/templates/setup.yml @@ -6,9 +6,11 @@ steps: pip install pytest pytest-cov pytest-runner coverage graphviz requests pyyaml pillow pandas parameterized sympy coloredlogs flatbuffers pip install $(CI_PIP_TF_NAME) $(CI_PIP_ONNX_NAME) - # protobuf release version 4.21.0 has some Python changes which is not compatible with tensorflow so far. + # Protobuf 3.20 results in linker errors on Windows in TF. + # Protobuf 4.0 is binary incompatible with what C++ TF uses. + # https://github.com/tensorflow/tensorflow/blob/c3337c73306b2b859d82fe130912f18e6a1c5c23/tensorflow/tools/pip_package/setup.py#L88 pip uninstall -y protobuf - pip install "protobuf<4.21.0" + pip install "protobuf<3.20.0" # TF < 2.7 reuires numpy <= 1.19, but onnxruntime >= 1.11 requires numpy >= 1.21 if [[ $CI_TF_VERSION < 2.7 ]] && [[ $CI_ONNX_BACKEND == "onnxruntime" ]] ; @@ -40,7 +42,12 @@ steps: if [[ $CI_TF_VERSION == 2.* ]] ; then - pip install onnxruntime-extensions==0.3.1 + # onnxruntime-extensions is not supported Python 3.10 so far. + # https://github.com/microsoft/onnxruntime-extensions/issues/273 + if [[ $CI_PYTHON_VERSION != 3.10 ]] ; + then + pip install onnxruntime-extensions==0.3.1 + fi if [[ $CI_TF_VERSION == 2.3* ]] ; then pip install tensorflow-text==${CI_TF_VERSION} @@ -64,6 +71,10 @@ steps: if [[ $CI_TF_VERSION == 2.8* ]] ; then pip install "tensorflow-text>=2.8,<2.9" + fi + if [[ $CI_TF_VERSION == 2.9* ]] ; + then + pip install "tensorflow-text>=2.9,<2.10" else pip install tensorflow-text fi diff --git a/ci_build/azure_pipelines/unit_test-matrix.yml b/ci_build/azure_pipelines/unit_test-matrix.yml index 4053c3729..8ff0ca47d 100644 --- a/ci_build/azure_pipelines/unit_test-matrix.yml +++ b/ci_build/azure_pipelines/unit_test-matrix.yml @@ -50,7 +50,7 @@ stages: - template: 'templates/job_generator.yml' parameters: platforms: ['linux', 'windows'] - python_versions: ['3.9'] + python_versions: ['3.9', '3.10'] tf_versions: ['2.8.0'] onnx_opsets: [''] job: diff --git a/ci_build/azure_pipelines/unit_test.yml b/ci_build/azure_pipelines/unit_test.yml index fa68a1d20..1996d9c55 100644 --- a/ci_build/azure_pipelines/unit_test.yml +++ b/ci_build/azure_pipelines/unit_test.yml @@ -106,7 +106,7 @@ stages: - template: 'templates/job_generator.yml' parameters: # tf 2.9 - python_versions: ['3.8'] + python_versions: ['3.10'] tf_versions: ['2.9.1'] onnx_opsets: [''] job: @@ -168,5 +168,16 @@ stages: - template: 'unit_test.yml' report_coverage: 'True' + - template: 'templates/job_generator.yml' + parameters: + python_versions: ['3.10'] + platforms: ['windows'] + tf_versions: ['2.9.1'] + onnx_opsets: ['16'] + job: + steps: + - template: 'unit_test.yml' + report_coverage: 'True' + - template: 'templates/combine_test_coverage.yml' diff --git a/setup.py b/setup.py index e4d5e2e52..2719e2207 100644 --- a/setup.py +++ b/setup.py @@ -97,5 +97,6 @@ def run(self): 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9'] + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10'] ) diff --git a/tests/test_backend.py b/tests/test_backend.py index a26f4fdba..cdbfeebc2 100755 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -3233,6 +3233,8 @@ def func(x, y): y_val = np.array(i / 10, np.float32) self._run_test_case(func, [_OUTPUT], {_INPUT: x_val, _INPUT1: y_val}, rtol=1e-6, atol=2e-5) + # https://github.com/microsoft/onnxruntime/issues/12302 + @skip_onnxruntime_backend("resize op can't work well under Cubic mode with ORT 1.12") @check_tf_min_version("2.0", "Results are slightly different in tf1") @check_opset_min_version(11, "resize bicubic") def test_resize_bicubic(self): From b65ae845f2e042dcc41926e7959cd9c0dd4a2e6d Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Fri, 29 Jul 2022 16:45:33 +0800 Subject: [PATCH 34/40] Increment main to 1.12 Signed-off-by: Deyu Huang --- VERSION_NUMBER | 2 +- tf2onnx/version.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION_NUMBER b/VERSION_NUMBER index 1cac385c6..0eed1a29e 100644 --- a/VERSION_NUMBER +++ b/VERSION_NUMBER @@ -1 +1 @@ -1.11.0 +1.12.0 diff --git a/tf2onnx/version.py b/tf2onnx/version.py index a1f101e25..10a9a9a23 100644 --- a/tf2onnx/version.py +++ b/tf2onnx/version.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -version = '1.11.0' -git_version = 'e9b6cb4fca61c87fcdaf4e9610535910c01ea4f9' +version = '1.12.0' +git_version = '087045d4b61e231897f1232de59609d30013b8f5' From a5878622309afa7205b5bd027d7d30604495a2a9 Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Fri, 29 Jul 2022 16:46:11 +0800 Subject: [PATCH 35/40] upgrade readme tf to 2.9 Signed-off-by: Deyu Huang --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d6555e77d..466fa8aa5 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ The common issues we run into we try to document here [Troubleshooting Guide](Tr | Build Type | OS | Python | TensorFlow | ONNX opset | Status | | --- | --- | --- | --- | --- | --- | -| Unit Test - Basic | Linux, MacOS\*, Windows\* | 3.7-3.10 | 1.13-1.15, 2.1-2.8 | 9-16 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=16&branchName=main) | -| Unit Test - Full | Linux, MacOS, Windows | 3.7-3.10 | 1.13-1.15, 2.1-2.8 | 9-16 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test-matrix?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=18&branchName=main) | | +| Unit Test - Basic | Linux, MacOS\*, Windows\* | 3.7-3.10 | 1.13-1.15, 2.1-2.9 | 9-16 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=16&branchName=main) | +| Unit Test - Full | Linux, MacOS, Windows | 3.7-3.10 | 1.13-1.15, 2.1-2.9 | 9-16 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test-matrix?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=18&branchName=main) | |
## Supported Versions From 6365d380c86bae9d89f29218e01d1fab78bce92c Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Tue, 2 Aug 2022 10:43:06 +0800 Subject: [PATCH 36/40] ONNX opset 17 with IR version 8 support (#2014) * ONNX opset 17 with IR version 8 support Signed-off-by: Deyu Huang --- README.md | 4 +- .../keras2onnx_application_tests.yml | 64 +-- .../azure_pipelines/keras2onnx_unit_test.yml | 64 +-- .../onnxruntime_nightly_test.yml | 38 +- .../pretrained_model_test-matrix.yml | 12 +- .../azure_pipelines/pretrained_model_test.yml | 4 +- .../templates/job_generator.yml | 2 +- .../azure_pipelines/templates/unit_test.yml | 2 +- .../trimmed_keras2onnx_application_tests.yml | 10 +- .../trimmed_keras2onnx_unit_test.yml | 22 +- ci_build/azure_pipelines/unit_test.yml | 2 +- support_status.md | 526 +++++++++--------- tests/run_pretrained_models.yaml | 4 +- tf2onnx/constants.py | 2 +- tools/gen_doc.py | 2 +- 15 files changed, 362 insertions(+), 396 deletions(-) diff --git a/README.md b/README.md index 466fa8aa5..d94f663a7 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ The common issues we run into we try to document here [Troubleshooting Guide](Tr tf2onnx will use the ONNX version installed on your system and installs the latest ONNX version if none is found. -We support and test ONNX opset-9 to opset-16. opset-6 to opset-8 should work but we don't test them. +We support and test ONNX opset-9 to opset-17. opset-6 to opset-8 should work but we don't test them. By default we use ```opset-13``` for the resulting ONNX graph. If you want the graph to be generated with a specific opset, use ```--opset``` in the command line, for example ```--opset 13```. @@ -187,7 +187,7 @@ ONNX requires default values for graph inputs to be constant, while Tensorflow's #### --opset -By default we use the opset 13 to generate the graph. By specifying ```--opset``` the user can override the default to generate a graph with the desired opset. For example ```--opset 16``` would create a onnx graph that uses only ops available in opset 16. Because older opsets have in most cases fewer ops, some models might not convert on a older opset. +By default we use the opset 13 to generate the graph. By specifying ```--opset``` the user can override the default to generate a graph with the desired opset. For example ```--opset 17``` would create a onnx graph that uses only ops available in opset 17. Because older opsets have in most cases fewer ops, some models might not convert on a older opset. #### --dequantize diff --git a/ci_build/azure_pipelines/keras2onnx_application_tests.yml b/ci_build/azure_pipelines/keras2onnx_application_tests.yml index 149c8cb64..5a8bdf6c0 100644 --- a/ci_build/azure_pipelines/keras2onnx_application_tests.yml +++ b/ci_build/azure_pipelines/keras2onnx_application_tests.yml @@ -8,30 +8,6 @@ jobs: vmImage: 'ubuntu-latest' strategy: matrix: - Python37-onnx1.6: - python.version: '3.7' - ONNX_PATH: onnx==1.6.0 - INSTALL_KERAS: pip install keras==2.3.1 - UNINSTALL_KERAS: - INSTALL_TENSORFLOW: pip install tensorflow==1.15.0 - INSTALL_ORT: pip install onnxruntime==1.8.0 - INSTALL_KERAS_RESNET: pip install keras-resnet - INSTALL_TRANSFORMERS: - INSTALL_NUMPY: - NIGHTLY_BUILD_TEST: python run_all.py --exclude "test_keras_applications_v2.py" - - Python37-onnx1.9: - python.version: '3.7' - ONNX_PATH: onnx==1.9.0 - INSTALL_KERAS: pip install keras==2.3.1 - UNINSTALL_KERAS: - INSTALL_TENSORFLOW: pip install tensorflow==1.15.0 - INSTALL_ORT: pip install onnxruntime==1.9.0 - INSTALL_KERAS_RESNET: pip install keras-resnet - INSTALL_TRANSFORMERS: - INSTALL_NUMPY: - NIGHTLY_BUILD_TEST: python run_all.py --exclude "test_keras_applications_v2.py" - Python37-onnx1.11-tf1.15: python.version: '3.7' ONNX_PATH: onnx==1.11.0 @@ -68,6 +44,18 @@ jobs: INSTALL_NUMPY: NIGHTLY_BUILD_TEST: python run_all_v2.py + Python310-onnx1.12-tf2.9: + python.version: '3.10' + ONNX_PATH: onnx==1.12.0 + INSTALL_KERAS: + UNINSTALL_KERAS: + INSTALL_TENSORFLOW: pip install tensorflow==2.9.0 + INSTALL_ORT: pip install onnxruntime==1.12.0 + INSTALL_KERAS_RESNET: pip install keras-resnet + INSTALL_TRANSFORMERS: pip install transformers==4.12.0 + INSTALL_NUMPY: + NIGHTLY_BUILD_TEST: python run_all_v2.py + steps: - template: 'templates/keras2onnx_application_tests.yml' parameters: @@ -79,18 +67,6 @@ jobs: vmImage: 'windows-2019' strategy: matrix: - Python37-onnx1.6: - python.version: '3.7' - ONNX_PATH: onnx==1.6.0 - INSTALL_KERAS: pip install keras==2.3.1 - UNINSTALL_KERAS: - INSTALL_TENSORFLOW: pip install tensorflow==1.15.0 - INSTALL_ORT: pip install onnxruntime==1.9.0 - INSTALL_KERAS_RESNET: pip install keras-resnet - INSTALL_TRANSFORMERS: - INSTALL_NUMPY: pip install numpy==1.19.0 - NIGHTLY_BUILD_TEST: python run_all_v2.py --exclude "test_keras_applications_v2.py" - Python37-onnx1.9: python.version: '3.7' ONNX_PATH: onnx==1.9.0 @@ -115,18 +91,30 @@ jobs: INSTALL_NUMPY: pip install numpy==1.19.0 NIGHTLY_BUILD_TEST: python run_all_v2.py --exclude "test_keras_applications_v2.py" - Python38-onnx1.11-tf2.9: + Python38-onnx1.11-tf2.8: python.version: '3.8' ONNX_PATH: onnx==1.11.0 INSTALL_KERAS: UNINSTALL_KERAS: - INSTALL_TENSORFLOW: pip install tensorflow==2.9.0 + INSTALL_TENSORFLOW: pip install tensorflow==2.8.0 INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_KERAS_RESNET: pip install keras-resnet INSTALL_TRANSFORMERS: pip install transformers==3.4.0 INSTALL_NUMPY: NIGHTLY_BUILD_TEST: python run_all_v2.py + Python310-onnx1.12-tf2.9: + python.version: '3.10' + ONNX_PATH: onnx==1.12.0 + INSTALL_KERAS: + UNINSTALL_KERAS: + INSTALL_TENSORFLOW: pip install tensorflow==2.9.0 + INSTALL_ORT: pip install onnxruntime==1.12.0 + INSTALL_KERAS_RESNET: pip install keras-resnet + INSTALL_TRANSFORMERS: pip install transformers==4.12.0 + INSTALL_NUMPY: + NIGHTLY_BUILD_TEST: python run_all_v2.py + steps: - template: 'templates/keras2onnx_application_tests.yml' parameters: diff --git a/ci_build/azure_pipelines/keras2onnx_unit_test.yml b/ci_build/azure_pipelines/keras2onnx_unit_test.yml index f1997b6a7..f9ad0879e 100644 --- a/ci_build/azure_pipelines/keras2onnx_unit_test.yml +++ b/ci_build/azure_pipelines/keras2onnx_unit_test.yml @@ -16,20 +16,6 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.9.0 INSTALL_NUMPY: pip install numpy==1.19.0 - Python38-tf2.2: - python.version: '3.8' - ONNX_PATH: onnx==1.11.0 - TENSORFLOW_PATH: tensorflow-cpu==2.2.0 - INSTALL_ORT: pip install onnxruntime==1.11.0 - INSTALL_NUMPY: pip install numpy==1.19.0 - - Python38-tf2.3: - python.version: '3.8' - ONNX_PATH: onnx==1.11.0 - TENSORFLOW_PATH: tensorflow-cpu==2.3.0 - INSTALL_ORT: pip install onnxruntime==1.11.0 - INSTALL_NUMPY: pip install numpy==1.19.0 - Python38-tf2.5: python.version: '3.8' ONNX_PATH: onnx==1.11.0 @@ -37,13 +23,20 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 - Python38-tf2.8: - python.version: '3.8' + Python39-tf2.8: + python.version: '3.9' ONNX_PATH: onnx==1.11.0 TENSORFLOW_PATH: tensorflow-cpu==2.8.0 INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: + Python310-tf2.9: + python.version: '3.9' + ONNX_PATH: onnx==1.12.0 + TENSORFLOW_PATH: tensorflow-cpu==2.9.0 + INSTALL_ORT: pip install onnxruntime==1.12.0 + INSTALL_NUMPY: + ############ Pure Keras Unit Tests ############ Keras-Py37-tf1.15.0: python.version: '3.7' @@ -70,6 +63,14 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 + Keras-Py310-tf2.9.0: + python.version: '3.10' + ONNX_PATH: -i onnx==1.12.0 + KERAS: keras==2.9.0 + TENSORFLOW_PATH: tensorflow==2.9.0 + INSTALL_ORT: pip install onnxruntime==1.11.0 + INSTALL_NUMPY: pip install numpy==1.23.0 + steps: - template: 'templates/keras2onnx_unit_test.yml' parameters: @@ -88,20 +89,6 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.9.0 INSTALL_NUMPY: pip install numpy==1.19.0 - Python37-tf2.2: - python.version: '3.7' - ONNX_PATH: onnx==1.11.0 - TENSORFLOW_PATH: tensorflow-cpu==2.2.0 - INSTALL_ORT: pip install onnxruntime==1.11.0 - INSTALL_NUMPY: pip install numpy==1.19.0 - - Python37-tf2.3: - python.version: '3.7' - ONNX_PATH: onnx==1.11.0 - TENSORFLOW_PATH: tensorflow-cpu==2.3.0 - INSTALL_ORT: pip install onnxruntime==1.11.0 - INSTALL_NUMPY: pip install numpy==1.19.0 - Python37-tf2.5: python.version: '3.7' ONNX_PATH: onnx==1.11.0 @@ -109,13 +96,20 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 - Python37-tf2.8: + Python38-tf2.8: python.version: '3.8' ONNX_PATH: onnx==1.11.0 TENSORFLOW_PATH: tensorflow-cpu==2.8.0 INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: + Python310-tf2.9: + python.version: '3.10' + ONNX_PATH: onnx==1.12.0 + TENSORFLOW_PATH: tensorflow-cpu==2.9.0 + INSTALL_ORT: pip install onnxruntime==1.12.0 + INSTALL_NUMPY: + ############ Pure Keras Unit Tests ############ Keras-Py37-tf1.15.0: python.version: '3.7' @@ -140,6 +134,14 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 + Keras-Py310-tf2.9.0: + python.version: '3.10' + ONNX_PATH: onnx==1.12.0 + KERAS: keras==2.9.0 + TENSORFLOW_PATH: tensorflow==2.9.0 + INSTALL_ORT: pip install onnxruntime==1.12.0 + INSTALL_NUMPY: + steps: - template: 'templates/keras2onnx_unit_test.yml' parameters: diff --git a/ci_build/azure_pipelines/onnxruntime_nightly_test.yml b/ci_build/azure_pipelines/onnxruntime_nightly_test.yml index 41e8f32de..cc0bab8b7 100644 --- a/ci_build/azure_pipelines/onnxruntime_nightly_test.yml +++ b/ci_build/azure_pipelines/onnxruntime_nightly_test.yml @@ -42,31 +42,7 @@ stages: - template: 'templates/job_generator.yml' parameters: - platforms: ['linux'] - python_versions: ['3.9'] - tf_versions: ['2.9.1'] - onnx_opsets: [''] - onnx_backends: {onnxruntime: ['nightly']} - job: - steps: - - template: 'unit_test.yml' - report_coverage: 'True' - - - template: 'templates/job_generator.yml' - parameters: - platforms: ['linux'] - python_versions: ['3.9'] - tf_versions: ['2.8.0'] - onnx_opsets: [''] - onnx_backends: {onnxruntime: ['nightly']} - job: - steps: - - template: 'unit_test.yml' - report_coverage: 'True' - - - template: 'templates/job_generator.yml' - parameters: - platforms: ['linux'] + platforms: ['linux', 'windows'] python_versions: ['3.8'] tf_versions: ['2.7.3'] onnx_opsets: [''] @@ -78,9 +54,9 @@ stages: - template: 'templates/job_generator.yml' parameters: - platforms: ['linux'] + platforms: ['linux', 'windows'] python_versions: ['3.9'] - tf_versions: ['2.6.2'] + tf_versions: ['2.8.0'] onnx_opsets: [''] onnx_backends: {onnxruntime: ['nightly']} job: @@ -90,16 +66,16 @@ stages: - template: 'templates/job_generator.yml' parameters: - platforms: ['windows'] - python_versions: ['3.7'] - tf_versions: ['2.5.0'] + platforms: ['linux', 'windows'] + python_versions: ['3.10'] + tf_versions: ['2.9.1'] onnx_opsets: [''] onnx_backends: {onnxruntime: ['nightly']} job: steps: - template: 'unit_test.yml' report_coverage: 'True' - + - template: 'templates/combine_test_coverage.yml' schedules: diff --git a/ci_build/azure_pipelines/pretrained_model_test-matrix.yml b/ci_build/azure_pipelines/pretrained_model_test-matrix.yml index e726435f4..aaccbad31 100755 --- a/ci_build/azure_pipelines/pretrained_model_test-matrix.yml +++ b/ci_build/azure_pipelines/pretrained_model_test-matrix.yml @@ -22,8 +22,8 @@ jobs: - template: 'templates/job_generator.yml' parameters: platforms: ['linux', 'windows'] - python_versions: ['3.9'] - tf_versions: ['2.6.2'] + python_versions: ['3.8'] + tf_versions: ['2.7.0'] job: steps: - template: 'pretrained_model_test.yml' @@ -32,7 +32,7 @@ jobs: parameters: platforms: ['linux', 'windows'] python_versions: ['3.8'] - tf_versions: ['2.7.0'] + tf_versions: ['2.8.0'] job: steps: - template: 'pretrained_model_test.yml' @@ -40,8 +40,8 @@ jobs: - template: 'templates/job_generator.yml' parameters: platforms: ['linux', 'windows'] - python_versions: ['3.8'] - tf_versions: ['2.8.0'] + python_versions: ['3.9'] + tf_versions: ['2.9.1'] job: steps: - template: 'pretrained_model_test.yml' @@ -49,7 +49,7 @@ jobs: - template: 'templates/job_generator.yml' parameters: platforms: ['linux', 'windows'] - python_versions: ['3.9'] + python_versions: ['3.10'] tf_versions: ['2.9.1'] job: steps: diff --git a/ci_build/azure_pipelines/pretrained_model_test.yml b/ci_build/azure_pipelines/pretrained_model_test.yml index 6e9e6ed42..9183f6ad0 100644 --- a/ci_build/azure_pipelines/pretrained_model_test.yml +++ b/ci_build/azure_pipelines/pretrained_model_test.yml @@ -24,8 +24,8 @@ jobs: - template: 'templates/job_generator.yml' parameters: - # 2.9, tf - python_versions: ['3.9'] + # 2.10, tf + python_versions: ['3.10'] tf_versions: ['2.9.1'] job: steps: diff --git a/ci_build/azure_pipelines/templates/job_generator.yml b/ci_build/azure_pipelines/templates/job_generator.yml index c0f23a30a..1f66aa12d 100644 --- a/ci_build/azure_pipelines/templates/job_generator.yml +++ b/ci_build/azure_pipelines/templates/job_generator.yml @@ -5,7 +5,7 @@ parameters: python_versions: ['3.7'] tf_versions: [''] onnx_versions: [''] - onnx_opsets: ['16', '15', '14', '13', '12', '11', '10', '9'] + onnx_opsets: ['17', '16', '15', '14', '13', '12', '11', '10', '9'] onnx_backends: {onnxruntime: ['1.12.0']} job: {} run_setup: 'True' diff --git a/ci_build/azure_pipelines/templates/unit_test.yml b/ci_build/azure_pipelines/templates/unit_test.yml index b9fdd033a..ff39fc56a 100644 --- a/ci_build/azure_pipelines/templates/unit_test.yml +++ b/ci_build/azure_pipelines/templates/unit_test.yml @@ -1,7 +1,7 @@ # Run unit test parameters: - onnx_opsets: ['16', '15', '14', '13', '12', '11', '10', '9'] + onnx_opsets: ['17', '16', '15', '14', '13', '12', '11', '10', '9'] skip_tflite_tests: 'True' skip_tfjs_tests: 'True' skip_tf_tests: 'False' diff --git a/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml b/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml index 5191c6630..ac14bd48a 100644 --- a/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml +++ b/ci_build/azure_pipelines/trimmed_keras2onnx_application_tests.yml @@ -20,15 +20,15 @@ jobs: INSTALL_NUMPY: pip install numpy==1.19.0 NIGHTLY_BUILD_TEST: python run_all.py --exclude "test_keras_applications_v2.py" - Python38-tf2.x: - python.version: '3.8' - ONNX_PATH: onnx==1.11.0 + Python310-tf2.x: + python.version: '3.10' + ONNX_PATH: onnx==1.12.0 INSTALL_KERAS: UNINSTALL_KERAS: INSTALL_TENSORFLOW: pip install tensorflow==2.9.1 - INSTALL_ORT: pip install onnxruntime==1.11.0 + INSTALL_ORT: pip install onnxruntime==1.12.0 INSTALL_KERAS_RESNET: pip install keras-resnet - INSTALL_TRANSFORMERS: pip install transformers==3.4.0 + INSTALL_TRANSFORMERS: pip install transformers==4.2.0 INSTALL_NUMPY: NIGHTLY_BUILD_TEST: python run_all_v2.py diff --git a/ci_build/azure_pipelines/trimmed_keras2onnx_unit_test.yml b/ci_build/azure_pipelines/trimmed_keras2onnx_unit_test.yml index 12d4d6e73..aa42ece9a 100644 --- a/ci_build/azure_pipelines/trimmed_keras2onnx_unit_test.yml +++ b/ci_build/azure_pipelines/trimmed_keras2onnx_unit_test.yml @@ -41,11 +41,11 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 - Keras-Py38-tf2.9.0: - python.version: '3.8' - ONNX_PATH: -i onnx==1.11.0 + Keras-Py310-tf2.9.0: + python.version: '3.10' + ONNX_PATH: -i onnx==1.12.0 TENSORFLOW_PATH: tensorflow==2.9.0 - INSTALL_ORT: pip install onnxruntime==1.11.0 + INSTALL_ORT: pip install onnxruntime==1.12.0 INSTALL_NUMPY: steps: @@ -66,13 +66,6 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: pip install numpy==1.19.0 - Python37-tf2.3: - python.version: '3.7' - ONNX_PATH: onnx==1.11.0 - TENSORFLOW_PATH: tensorflow-cpu==2.3.0 - INSTALL_ORT: pip install onnxruntime==1.11.0 - INSTALL_NUMPY: pip install numpy==1.19.0 - Python38-tf2.9: python.version: '3.8' ONNX_PATH: onnx==1.11.0 @@ -80,6 +73,13 @@ jobs: INSTALL_ORT: pip install onnxruntime==1.11.0 INSTALL_NUMPY: + Python310-tf2.9: + python.version: '3.10' + ONNX_PATH: onnx==1.12.0 + TENSORFLOW_PATH: tensorflow-cpu==2.9.0 + INSTALL_ORT: pip install onnxruntime==1.12.0 + INSTALL_NUMPY: + ############ Pure Keras Unit Tests ############ Keras-Py37-tf2.2.0: python.version: '3.7' diff --git a/ci_build/azure_pipelines/unit_test.yml b/ci_build/azure_pipelines/unit_test.yml index 1996d9c55..47ebf15ed 100644 --- a/ci_build/azure_pipelines/unit_test.yml +++ b/ci_build/azure_pipelines/unit_test.yml @@ -173,7 +173,7 @@ stages: python_versions: ['3.10'] platforms: ['windows'] tf_versions: ['2.9.1'] - onnx_opsets: ['16'] + onnx_opsets: ['17'] job: steps: - template: 'unit_test.yml' diff --git a/support_status.md b/support_status.md index 4586c3d12..f4f736539 100644 --- a/support_status.md +++ b/support_status.md @@ -4,269 +4,269 @@ ### Domain: "" (default domain) | Tensorflow Op | Convertible to ONNX Op Versions | | ------------- | ------------------------------- | -| Abs | 1 ~ 16 | -| Acos | 7 ~ 16 | -| Acosh | 9 ~ 16 | -| Add | 1 ~ 16 | -| AddN | 6 ~ 16 | -| AddV2 | 1 ~ 16 | -| AdjustContrastv2 | 1 ~ 16 | -| AdjustHue | 11 ~ 16 | -| AdjustSaturation | 11 ~ 16 | -| All | 6 ~ 16 | -| Any | 6 ~ 16 | -| ArgMax | 1 ~ 16 | -| ArgMin | 1 ~ 16 | -| AsString | 9 ~ 16 | -| Asin | 7 ~ 16 | -| Asinh | 9 ~ 16 | -| Atan | 7 ~ 16 | -| Atan2 | 9 ~ 16 | -| Atanh | 9 ~ 16 | -| AvgPool | 1 ~ 16 | -| AvgPool3D | 1 ~ 16 | -| BatchMatMul | 1 ~ 16 | -| BatchMatMulV2 | 1 ~ 16 | -| BatchToSpaceND | 1 ~ 16 | -| BiasAdd | 1 ~ 16 | -| BiasAddV1 | 1 ~ 16 | -| Bincount | 11 ~ 16 | -| BroadcastTo | 8 ~ 16 | -| CTCGreedyDecoder | 11 ~ 16 | -| Cast | 1 ~ 16 | -| Ceil | 1 ~ 16 | -| CheckNumerics | 1 ~ 16 | -| ClipByValue | 8 ~ 16 | -| CombinedNonMaxSuppression | 12 ~ 16 | -| ComplexAbs | 1 ~ 16 | -| Concat | 1 ~ 16 | -| ConcatV2 | 1 ~ 16 | -| Const | 1 ~ 16 | -| ConstV2 | 1 ~ 16 | -| Conv1D | 1 ~ 16 | -| Conv2D | 1 ~ 16 | -| Conv2DBackpropInput | 1 ~ 16 | -| Conv3D | 1 ~ 16 | -| Conv3DBackpropInputV2 | 1 ~ 16 | -| Cos | 7 ~ 16 | -| Cosh | 9 ~ 16 | -| CropAndResize | 10 ~ 16 | -| CudnnRNN | 10 ~ 16 | -| Cumsum | 11 ~ 16 | -| DenseBincount | 11 ~ 16 | -| DenseToDenseSetOperation | 11 ~ 16 | -| DepthToSpace | 1 ~ 16 | -| DepthwiseConv2d | 1 ~ 16 | -| DepthwiseConv2dNative | 1 ~ 16 | -| Div | 1 ~ 16 | -| DivNoNan | 9 ~ 16 | -| Dropout | 1 ~ 16 | -| DynamicPartition | 9 ~ 16 | -| DynamicStitch | 10 ~ 16 | -| Einsum | 12 ~ 16 | -| Elu | 1 ~ 16 | -| EnsureShape | 1 ~ 16 | -| Equal | 1 ~ 16 | -| Erf | 1 ~ 16 | -| Exp | 1 ~ 16 | -| ExpandDims | 1 ~ 16 | -| FFT | 1 ~ 16 | -| FIFOQueueV2 | 8 ~ 16 | -| FakeQuantWithMinMaxArgs | 10 ~ 16 | -| FakeQuantWithMinMaxVars | 10 ~ 16 | -| Fill | 7 ~ 16 | -| Flatten | 1 ~ 16 | -| Floor | 1 ~ 16 | -| FloorDiv | 6 ~ 16 | -| FloorMod | 7 ~ 16 | -| FusedBatchNorm | 6 ~ 16 | -| FusedBatchNormV2 | 6 ~ 16 | -| FusedBatchNormV3 | 6 ~ 16 | -| Gather | 1 ~ 16 | -| GatherNd | 1 ~ 16 | -| GatherV2 | 1 ~ 16 | -| Greater | 1 ~ 16 | -| GreaterEqual | 7 ~ 16 | -| HardSwish | 14 ~ 16 | -| HashTableV2 | 8 ~ 16 | -| Identity | 1 ~ 16 | -| IdentityN | 1 ~ 16 | -| If | 1 ~ 16 | -| InvertPermutation | 11 ~ 16 | -| IsFinite | 10 ~ 16 | -| IsInf | 10 ~ 16 | -| IsNan | 9 ~ 16 | -| IteratorGetNext | 8 ~ 16 | -| IteratorV2 | 8 ~ 16 | -| LRN | 1 ~ 16 | -| LSTMBlockCell | 1 ~ 16 | -| LeakyRelu | 1 ~ 16 | -| LeftShift | 11 ~ 16 | -| Less | 1 ~ 16 | -| LessEqual | 7 ~ 16 | -| Log | 1 ~ 16 | -| LogSoftmax | 1 ~ 16 | -| LogicalAnd | 1 ~ 16 | -| LogicalNot | 1 ~ 16 | -| LogicalOr | 1 ~ 16 | -| LookupTableFindV2 | 8 ~ 16 | -| LookupTableSizeV2 | 1 ~ 16 | -| Loop | 7 ~ 16 | -| MatMul | 1 ~ 16 | -| MatrixBandPart | 7 ~ 16 | -| MatrixDeterminant | 11 ~ 16 | -| MatrixDiag | 12 ~ 16 | -| MatrixDiagPart | 11 ~ 16 | -| MatrixDiagPartV2 | 11 ~ 16 | -| MatrixDiagPartV3 | 11 ~ 16 | -| MatrixDiagV2 | 12 ~ 16 | -| MatrixDiagV3 | 12 ~ 16 | -| MatrixSetDiagV3 | 12 ~ 16 | -| Max | 1 ~ 16 | -| MaxPool | 1 ~ 16 | -| MaxPool3D | 1 ~ 16 | -| MaxPoolV2 | 1 ~ 16 | -| MaxPoolWithArgmax | 8 ~ 16 | -| Maximum | 1 ~ 16 | -| Mean | 1 ~ 16 | -| Min | 1 ~ 16 | -| Minimum | 1 ~ 16 | -| MirrorPad | 1 ~ 16 | -| Mul | 1 ~ 16 | -| Multinomial | 7 ~ 16 | -| Neg | 1 ~ 16 | -| NoOp | 1 ~ 16 | -| NonMaxSuppressionV2 | 10 ~ 16 | -| NonMaxSuppressionV3 | 10 ~ 16 | -| NonMaxSuppressionV4 | 10 ~ 16 | -| NonMaxSuppressionV5 | 10 ~ 16 | -| NotEqual | 1 ~ 16 | -| OneHot | 1 ~ 16 | -| Pack | 1 ~ 16 | -| Pad | 1 ~ 16 | -| PadV2 | 1 ~ 16 | -| ParallelDynamicStitch | 10 ~ 16 | -| Placeholder | 1 ~ 16 | -| PlaceholderV2 | 1 ~ 16 | -| PlaceholderWithDefault | 1 ~ 16 | -| Pow | 1 ~ 16 | -| Prelu | 1 ~ 16 | -| Prod | 1 ~ 16 | -| QueueDequeueManyV2 | 8 ~ 16 | -| QueueDequeueUpToV2 | 8 ~ 16 | -| QueueDequeueV2 | 8 ~ 16 | -| RFFT | 1 ~ 16 | -| RFFT2D | 1 ~ 16 | -| RaggedGather | 11 ~ 16 | -| RaggedRange | 11 ~ 16 | -| RaggedTensorFromVariant | 13 ~ 16 | -| RaggedTensorToSparse | 11 ~ 16 | -| RaggedTensorToTensor | 11 ~ 16 | -| RaggedTensorToVariant | 13 ~ 16 | -| RandomNormal | 1 ~ 16 | -| RandomNormalLike | 1 ~ 16 | -| RandomShuffle | 10 ~ 16 | -| RandomStandardNormal | 1 ~ 16 | -| RandomUniform | 1 ~ 16 | -| RandomUniformInt | 1 ~ 16 | -| RandomUniformLike | 1 ~ 16 | -| Range | 7 ~ 16 | -| RealDiv | 1 ~ 16 | -| Reciprocal | 1 ~ 16 | -| Relu | 1 ~ 16 | -| Relu6 | 1 ~ 16 | -| Reshape | 1 ~ 16 | -| ResizeBicubic | 7 ~ 16 | -| ResizeBilinear | 7 ~ 16 | -| ResizeNearestNeighbor | 7 ~ 16 | -| ReverseSequence | 8 ~ 16 (Except 9) | -| ReverseV2 | 10 ~ 16 | -| RightShift | 11 ~ 16 | -| Rint | 11 ~ 16 | -| Roll | 10 ~ 16 | -| Round | 1 ~ 16 | -| Rsqrt | 1 ~ 16 | -| SampleDistortedBoundingBox | 9 ~ 16 | -| SampleDistortedBoundingBoxV2 | 9 ~ 16 | -| Scan | 7 ~ 16 | -| ScatterNd | 11 ~ 16 | -| SegmentMax | 11 ~ 16 | -| SegmentMean | 11 ~ 16 | -| SegmentMin | 11 ~ 16 | -| SegmentProd | 11 ~ 16 | -| SegmentSum | 11 ~ 16 | -| Select | 7 ~ 16 | -| SelectV2 | 7 ~ 16 | -| Selu | 1 ~ 16 | -| Shape | 1 ~ 16 | -| Sigmoid | 1 ~ 16 | -| Sign | 1 ~ 16 | -| Sin | 7 ~ 16 | -| Sinh | 9 ~ 16 | -| Size | 1 ~ 16 | -| Slice | 1 ~ 16 | -| Softmax | 1 ~ 16 | -| SoftmaxCrossEntropyWithLogits | 7 ~ 16 | -| Softplus | 1 ~ 16 | -| Softsign | 1 ~ 16 | -| SpaceToBatchND | 1 ~ 16 | -| SpaceToDepth | 1 ~ 16 | -| SparseFillEmptyRows | 11 ~ 16 | -| SparseReshape | 11 ~ 16 | -| SparseSegmentMean | 11 ~ 16 | -| SparseSegmentMeanWithNumSegments | 11 ~ 16 | -| SparseSegmentSqrtN | 11 ~ 16 | -| SparseSegmentSqrtNWithNumSegments | 11 ~ 16 | -| SparseSegmentSum | 11 ~ 16 | -| SparseSegmentSumWithNumSegments | 11 ~ 16 | -| SparseSoftmaxCrossEntropyWithLogits | 7 ~ 16 | -| SparseToDense | 11 ~ 16 | -| Split | 1 ~ 16 | -| SplitV | 1 ~ 16 | -| Sqrt | 1 ~ 16 | -| Square | 1 ~ 16 | -| SquaredDifference | 1 ~ 16 | -| SquaredDistance | 12 ~ 16 | -| Squeeze | 1 ~ 16 | -| StatelessIf | 1 ~ 16 | -| StatelessWhile | 7 ~ 16 | -| StopGradient | 1 ~ 16 | -| StridedSlice | 1 ~ 16 | -| StringLower | 10 ~ 16 | -| StringToNumber | 9 ~ 16 | -| StringUpper | 10 ~ 16 | -| Sub | 1 ~ 16 | -| Sum | 1 ~ 16 | -| TFL_CONCATENATION | 1 ~ 16 | -| TFL_DEQUANTIZE | 1 ~ 16 | -| TFL_PRELU | 7 ~ 16 | -| TFL_QUANTIZE | 1 ~ 16 | -| TFL_TFLite_Detection_PostProcess | 11 ~ 16 | -| TFL_WHILE | 7 ~ 16 | -| Tan | 7 ~ 16 | -| Tanh | 1 ~ 16 | -| TensorListFromTensor | 7 ~ 16 | -| TensorListGetItem | 7 ~ 16 | -| TensorListLength | 7 ~ 16 | -| TensorListReserve | 7 ~ 16 | -| TensorListResize | 7 ~ 16 | -| TensorListSetItem | 7 ~ 16 | -| TensorListStack | 7 ~ 16 | -| TensorScatterAdd | 16 | -| TensorScatterUpdate | 11 ~ 16 | -| Tile | 1 ~ 16 | -| TopKV2 | 1 ~ 16 | -| Transpose | 1 ~ 16 | -| TruncateDiv | 1 ~ 16 | -| Unique | 11 ~ 16 | -| Unpack | 1 ~ 16 | -| UnsortedSegmentMax | 11 ~ 16 | -| UnsortedSegmentMin | 11 ~ 16 | -| UnsortedSegmentProd | 11 ~ 16 | -| UnsortedSegmentSum | 11 ~ 16 | -| Where | 9 ~ 16 | -| While | 7 ~ 16 | -| ZerosLike | 1 ~ 16 | +| Abs | 1 ~ 17 | +| Acos | 7 ~ 17 | +| Acosh | 9 ~ 17 | +| Add | 1 ~ 17 | +| AddN | 6 ~ 17 | +| AddV2 | 1 ~ 17 | +| AdjustContrastv2 | 1 ~ 17 | +| AdjustHue | 11 ~ 17 | +| AdjustSaturation | 11 ~ 17 | +| All | 6 ~ 17 | +| Any | 6 ~ 17 | +| ArgMax | 1 ~ 17 | +| ArgMin | 1 ~ 17 | +| AsString | 9 ~ 17 | +| Asin | 7 ~ 17 | +| Asinh | 9 ~ 17 | +| Atan | 7 ~ 17 | +| Atan2 | 9 ~ 17 | +| Atanh | 9 ~ 17 | +| AvgPool | 1 ~ 17 | +| AvgPool3D | 1 ~ 17 | +| BatchMatMul | 1 ~ 17 | +| BatchMatMulV2 | 1 ~ 17 | +| BatchToSpaceND | 1 ~ 17 | +| BiasAdd | 1 ~ 17 | +| BiasAddV1 | 1 ~ 17 | +| Bincount | 11 ~ 17 | +| BroadcastTo | 8 ~ 17 | +| CTCGreedyDecoder | 11 ~ 17 | +| Cast | 1 ~ 17 | +| Ceil | 1 ~ 17 | +| CheckNumerics | 1 ~ 17 | +| ClipByValue | 8 ~ 17 | +| CombinedNonMaxSuppression | 12 ~ 17 | +| ComplexAbs | 1 ~ 17 | +| Concat | 1 ~ 17 | +| ConcatV2 | 1 ~ 17 | +| Const | 1 ~ 17 | +| ConstV2 | 1 ~ 17 | +| Conv1D | 1 ~ 17 | +| Conv2D | 1 ~ 17 | +| Conv2DBackpropInput | 1 ~ 17 | +| Conv3D | 1 ~ 17 | +| Conv3DBackpropInputV2 | 1 ~ 17 | +| Cos | 7 ~ 17 | +| Cosh | 9 ~ 17 | +| CropAndResize | 10 ~ 17 | +| CudnnRNN | 10 ~ 17 | +| Cumsum | 11 ~ 17 | +| DenseBincount | 11 ~ 17 | +| DenseToDenseSetOperation | 11 ~ 17 | +| DepthToSpace | 1 ~ 17 | +| DepthwiseConv2d | 1 ~ 17 | +| DepthwiseConv2dNative | 1 ~ 17 | +| Div | 1 ~ 17 | +| DivNoNan | 9 ~ 17 | +| Dropout | 1 ~ 17 | +| DynamicPartition | 9 ~ 17 | +| DynamicStitch | 10 ~ 17 | +| Einsum | 12 ~ 17 | +| Elu | 1 ~ 17 | +| EnsureShape | 1 ~ 17 | +| Equal | 1 ~ 17 | +| Erf | 1 ~ 17 | +| Exp | 1 ~ 17 | +| ExpandDims | 1 ~ 17 | +| FFT | 1 ~ 17 | +| FIFOQueueV2 | 8 ~ 17 | +| FakeQuantWithMinMaxArgs | 10 ~ 17 | +| FakeQuantWithMinMaxVars | 10 ~ 17 | +| Fill | 7 ~ 17 | +| Flatten | 1 ~ 17 | +| Floor | 1 ~ 17 | +| FloorDiv | 6 ~ 17 | +| FloorMod | 7 ~ 17 | +| FusedBatchNorm | 6 ~ 17 | +| FusedBatchNormV2 | 6 ~ 17 | +| FusedBatchNormV3 | 6 ~ 17 | +| Gather | 1 ~ 17 | +| GatherNd | 1 ~ 17 | +| GatherV2 | 1 ~ 17 | +| Greater | 1 ~ 17 | +| GreaterEqual | 7 ~ 17 | +| HardSwish | 14 ~ 17 | +| HashTableV2 | 8 ~ 17 | +| Identity | 1 ~ 17 | +| IdentityN | 1 ~ 17 | +| If | 1 ~ 17 | +| InvertPermutation | 11 ~ 17 | +| IsFinite | 10 ~ 17 | +| IsInf | 10 ~ 17 | +| IsNan | 9 ~ 17 | +| IteratorGetNext | 8 ~ 17 | +| IteratorV2 | 8 ~ 17 | +| LRN | 1 ~ 17 | +| LSTMBlockCell | 1 ~ 17 | +| LeakyRelu | 1 ~ 17 | +| LeftShift | 11 ~ 17 | +| Less | 1 ~ 17 | +| LessEqual | 7 ~ 17 | +| Log | 1 ~ 17 | +| LogSoftmax | 1 ~ 17 | +| LogicalAnd | 1 ~ 17 | +| LogicalNot | 1 ~ 17 | +| LogicalOr | 1 ~ 17 | +| LookupTableFindV2 | 8 ~ 17 | +| LookupTableSizeV2 | 1 ~ 17 | +| Loop | 7 ~ 17 | +| MatMul | 1 ~ 17 | +| MatrixBandPart | 7 ~ 17 | +| MatrixDeterminant | 11 ~ 17 | +| MatrixDiag | 12 ~ 17 | +| MatrixDiagPart | 11 ~ 17 | +| MatrixDiagPartV2 | 11 ~ 17 | +| MatrixDiagPartV3 | 11 ~ 17 | +| MatrixDiagV2 | 12 ~ 17 | +| MatrixDiagV3 | 12 ~ 17 | +| MatrixSetDiagV3 | 12 ~ 17 | +| Max | 1 ~ 17 | +| MaxPool | 1 ~ 17 | +| MaxPool3D | 1 ~ 17 | +| MaxPoolV2 | 1 ~ 17 | +| MaxPoolWithArgmax | 8 ~ 17 | +| Maximum | 1 ~ 17 | +| Mean | 1 ~ 17 | +| Min | 1 ~ 17 | +| Minimum | 1 ~ 17 | +| MirrorPad | 1 ~ 17 | +| Mul | 1 ~ 17 | +| Multinomial | 7 ~ 17 | +| Neg | 1 ~ 17 | +| NoOp | 1 ~ 17 | +| NonMaxSuppressionV2 | 10 ~ 17 | +| NonMaxSuppressionV3 | 10 ~ 17 | +| NonMaxSuppressionV4 | 10 ~ 17 | +| NonMaxSuppressionV5 | 10 ~ 17 | +| NotEqual | 1 ~ 17 | +| OneHot | 1 ~ 17 | +| Pack | 1 ~ 17 | +| Pad | 1 ~ 17 | +| PadV2 | 1 ~ 17 | +| ParallelDynamicStitch | 10 ~ 17 | +| Placeholder | 1 ~ 17 | +| PlaceholderV2 | 1 ~ 17 | +| PlaceholderWithDefault | 1 ~ 17 | +| Pow | 1 ~ 17 | +| Prelu | 1 ~ 17 | +| Prod | 1 ~ 17 | +| QueueDequeueManyV2 | 8 ~ 17 | +| QueueDequeueUpToV2 | 8 ~ 17 | +| QueueDequeueV2 | 8 ~ 17 | +| RFFT | 1 ~ 17 | +| RFFT2D | 1 ~ 17 | +| RaggedGather | 11 ~ 17 | +| RaggedRange | 11 ~ 17 | +| RaggedTensorFromVariant | 13 ~ 17 | +| RaggedTensorToSparse | 11 ~ 17 | +| RaggedTensorToTensor | 11 ~ 17 | +| RaggedTensorToVariant | 13 ~ 17 | +| RandomNormal | 1 ~ 17 | +| RandomNormalLike | 1 ~ 17 | +| RandomShuffle | 10 ~ 17 | +| RandomStandardNormal | 1 ~ 17 | +| RandomUniform | 1 ~ 17 | +| RandomUniformInt | 1 ~ 17 | +| RandomUniformLike | 1 ~ 17 | +| Range | 7 ~ 17 | +| RealDiv | 1 ~ 17 | +| Reciprocal | 1 ~ 17 | +| Relu | 1 ~ 17 | +| Relu6 | 1 ~ 17 | +| Reshape | 1 ~ 17 | +| ResizeBicubic | 7 ~ 17 | +| ResizeBilinear | 7 ~ 17 | +| ResizeNearestNeighbor | 7 ~ 17 | +| ReverseSequence | 8 ~ 17 (Except 9) | +| ReverseV2 | 10 ~ 17 | +| RightShift | 11 ~ 17 | +| Rint | 11 ~ 17 | +| Roll | 10 ~ 17 | +| Round | 1 ~ 17 | +| Rsqrt | 1 ~ 17 | +| SampleDistortedBoundingBox | 9 ~ 17 | +| SampleDistortedBoundingBoxV2 | 9 ~ 17 | +| Scan | 7 ~ 17 | +| ScatterNd | 11 ~ 17 | +| SegmentMax | 11 ~ 17 | +| SegmentMean | 11 ~ 17 | +| SegmentMin | 11 ~ 17 | +| SegmentProd | 11 ~ 17 | +| SegmentSum | 11 ~ 17 | +| Select | 7 ~ 17 | +| SelectV2 | 7 ~ 17 | +| Selu | 1 ~ 17 | +| Shape | 1 ~ 17 | +| Sigmoid | 1 ~ 17 | +| Sign | 1 ~ 17 | +| Sin | 7 ~ 17 | +| Sinh | 9 ~ 17 | +| Size | 1 ~ 17 | +| Slice | 1 ~ 17 | +| Softmax | 1 ~ 17 | +| SoftmaxCrossEntropyWithLogits | 7 ~ 17 | +| Softplus | 1 ~ 17 | +| Softsign | 1 ~ 17 | +| SpaceToBatchND | 1 ~ 17 | +| SpaceToDepth | 1 ~ 17 | +| SparseFillEmptyRows | 11 ~ 17 | +| SparseReshape | 11 ~ 17 | +| SparseSegmentMean | 11 ~ 17 | +| SparseSegmentMeanWithNumSegments | 11 ~ 17 | +| SparseSegmentSqrtN | 11 ~ 17 | +| SparseSegmentSqrtNWithNumSegments | 11 ~ 17 | +| SparseSegmentSum | 11 ~ 17 | +| SparseSegmentSumWithNumSegments | 11 ~ 17 | +| SparseSoftmaxCrossEntropyWithLogits | 7 ~ 17 | +| SparseToDense | 11 ~ 17 | +| Split | 1 ~ 17 | +| SplitV | 1 ~ 17 | +| Sqrt | 1 ~ 17 | +| Square | 1 ~ 17 | +| SquaredDifference | 1 ~ 17 | +| SquaredDistance | 12 ~ 17 | +| Squeeze | 1 ~ 17 | +| StatelessIf | 1 ~ 17 | +| StatelessWhile | 7 ~ 17 | +| StopGradient | 1 ~ 17 | +| StridedSlice | 1 ~ 17 | +| StringLower | 10 ~ 17 | +| StringToNumber | 9 ~ 17 | +| StringUpper | 10 ~ 17 | +| Sub | 1 ~ 17 | +| Sum | 1 ~ 17 | +| TFL_CONCATENATION | 1 ~ 17 | +| TFL_DEQUANTIZE | 1 ~ 17 | +| TFL_PRELU | 7 ~ 17 | +| TFL_QUANTIZE | 1 ~ 17 | +| TFL_TFLite_Detection_PostProcess | 11 ~ 17 | +| TFL_WHILE | 7 ~ 17 | +| Tan | 7 ~ 17 | +| Tanh | 1 ~ 17 | +| TensorListFromTensor | 7 ~ 17 | +| TensorListGetItem | 7 ~ 17 | +| TensorListLength | 7 ~ 17 | +| TensorListReserve | 7 ~ 17 | +| TensorListResize | 7 ~ 17 | +| TensorListSetItem | 7 ~ 17 | +| TensorListStack | 7 ~ 17 | +| TensorScatterAdd | 16 ~ 17 | +| TensorScatterUpdate | 11 ~ 17 | +| Tile | 1 ~ 17 | +| TopKV2 | 1 ~ 17 | +| Transpose | 1 ~ 17 | +| TruncateDiv | 1 ~ 17 | +| Unique | 11 ~ 17 | +| Unpack | 1 ~ 17 | +| UnsortedSegmentMax | 11 ~ 17 | +| UnsortedSegmentMin | 11 ~ 17 | +| UnsortedSegmentProd | 11 ~ 17 | +| UnsortedSegmentSum | 11 ~ 17 | +| Where | 9 ~ 17 | +| While | 7 ~ 17 | +| ZerosLike | 1 ~ 17 | ### Domain: "com.google.tensorflow" | Tensorflow Op | Convertible to ONNX Op Versions | | ------------- | ------------------------------- | diff --git a/tests/run_pretrained_models.yaml b/tests/run_pretrained_models.yaml index 3a195cf51..38fa639fb 100644 --- a/tests/run_pretrained_models.yaml +++ b/tests/run_pretrained_models.yaml @@ -337,7 +337,7 @@ ssd_mobilenet_v3_large_coco: opset_constraints: "onnx": "min": 10 - "max": 16 + "max": 17 input_get: get_beach inputs: "normalized_input_image_tensor:0": [1, 320, 320, 3] @@ -432,7 +432,7 @@ faster_rcnn_inception_v2_coco: opset_constraints: "onnx": "min": 11 - "max": 16 + "max": 17 input_get: get_beach inputs: "image_tensor:0": [1, 224, 224, 3] diff --git a/tf2onnx/constants.py b/tf2onnx/constants.py index 9a7005453..c1314d0a8 100644 --- a/tf2onnx/constants.py +++ b/tf2onnx/constants.py @@ -55,5 +55,5 @@ # Note: opset 7 and opset 8 came out with IR3 but we need IR4 because of PlaceholderWithDefault # Refer from https://github.com/onnx/onnx/blob/main/docs/Versioning.md#released-versions OPSET_TO_IR_VERSION = { - 1: 3, 2: 3, 3: 3, 4: 3, 5: 3, 6: 3, 7: 4, 8: 4, 9: 4, 10: 5, 11: 6, 12: 7, 13: 7, 14: 7, 15: 8, 16: 8 + 1: 3, 2: 3, 3: 3, 4: 3, 5: 3, 6: 3, 7: 4, 8: 4, 9: 4, 10: 5, 11: 6, 12: 7, 13: 7, 14: 7, 15: 8, 16: 8, 17: 8 } diff --git a/tools/gen_doc.py b/tools/gen_doc.py index 50b6f6708..fa2497fcb 100644 --- a/tools/gen_doc.py +++ b/tools/gen_doc.py @@ -20,7 +20,7 @@ LATEST_OPSET = { - "": 16, # default domain + "": 17, # default domain "com.microsoft": 1, # microsoft domain "ai.onnx.contrib": 1, # contrib ops } From 0bfdf63a2dc851bcde623d49c82b24d43f650a6c Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Tue, 2 Aug 2022 16:14:56 +0800 Subject: [PATCH 37/40] Remove opset below to 13 ci tests and enhance doc Signed-off-by: Deyu Huang --- README.md | 11 +++++------ .../azure_pipelines/templates/job_generator.yml | 2 +- ci_build/azure_pipelines/templates/unit_test.yml | 2 +- ci_build/azure_pipelines/unit_test-matrix.yml | 13 +------------ ci_build/azure_pipelines/unit_test.yml | 6 +++--- 5 files changed, 11 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index d94f663a7..912f3101f 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ The common issues we run into we try to document here [Troubleshooting Guide](Tr | Build Type | OS | Python | TensorFlow | ONNX opset | Status | | --- | --- | --- | --- | --- | --- | -| Unit Test - Basic | Linux, MacOS\*, Windows\* | 3.7-3.10 | 1.13-1.15, 2.1-2.9 | 9-16 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=16&branchName=main) | -| Unit Test - Full | Linux, MacOS, Windows | 3.7-3.10 | 1.13-1.15, 2.1-2.9 | 9-16 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test-matrix?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=18&branchName=main) | | +| Unit Test - Basic | Linux, MacOS\*, Windows\* | 3.7-3.10 | 1.13-1.15, 2.1-2.9 | 13-17 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=16&branchName=main) | +| Unit Test - Full | Linux, MacOS, Windows | 3.7-3.10 | 1.13-1.15, 2.1-2.9 | 13-17 | [![Build Status](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_apis/build/status/unit_test-matrix?branchName=main)](https://dev.azure.com/tensorflow-onnx/tensorflow-onnx/_build/latest?definitionId=18&branchName=main) | |
## Supported Versions @@ -27,7 +27,7 @@ The common issues we run into we try to document here [Troubleshooting Guide](Tr tf2onnx will use the ONNX version installed on your system and installs the latest ONNX version if none is found. -We support and test ONNX opset-9 to opset-17. opset-6 to opset-8 should work but we don't test them. +We support and test ONNX opset-13 to opset-17. opset-6 to opset-12 should work but we don't test them. By default we use ```opset-13``` for the resulting ONNX graph. If you want the graph to be generated with a specific opset, use ```--opset``` in the command line, for example ```--opset 13```. @@ -43,7 +43,6 @@ You can install tf2onnx on top of tf-1.x or tf-2.x. ### Python We support Python ```3.7-3.10```. -Note that on windows for Python > 3.7 the protobuf package doesn't use the cpp implementation and is very slow - we recommend to use Python 3.7 for that reason. ## Prerequisites @@ -83,7 +82,7 @@ or ```python setup.py develop``` -tensorflow-onnx requires onnx-1.5 or better and will install/upgrade onnx if needed. +tensorflow-onnx requires onnx-1.9 or better and will install/upgrade onnx if needed. To create a wheel for distribution: @@ -100,7 +99,7 @@ To get started with `tensorflow-onnx`, run the `t2onnx.convert` command, providi The above command uses a default of `13` for the ONNX opset. If you need a newer opset, or want to limit your model to use an older opset then you can provide the `--opset` argument to the command. If you are unsure about which opset to use, refer to the [ONNX operator documentation](https://github.com/onnx/onnx/releases). -```python -m tf2onnx.convert --saved-model tensorflow-model-path --opset 16 --output model.onnx``` +```python -m tf2onnx.convert --saved-model tensorflow-model-path --opset 17 --output model.onnx``` If your TensorFlow model is in a format other than `saved model`, then you need to provide the inputs and outputs of the model graph. diff --git a/ci_build/azure_pipelines/templates/job_generator.yml b/ci_build/azure_pipelines/templates/job_generator.yml index 1f66aa12d..dcbc08e79 100644 --- a/ci_build/azure_pipelines/templates/job_generator.yml +++ b/ci_build/azure_pipelines/templates/job_generator.yml @@ -5,7 +5,7 @@ parameters: python_versions: ['3.7'] tf_versions: [''] onnx_versions: [''] - onnx_opsets: ['17', '16', '15', '14', '13', '12', '11', '10', '9'] + onnx_opsets: ['17', '16', '15', '14', '13'] onnx_backends: {onnxruntime: ['1.12.0']} job: {} run_setup: 'True' diff --git a/ci_build/azure_pipelines/templates/unit_test.yml b/ci_build/azure_pipelines/templates/unit_test.yml index ff39fc56a..9eb444665 100644 --- a/ci_build/azure_pipelines/templates/unit_test.yml +++ b/ci_build/azure_pipelines/templates/unit_test.yml @@ -1,7 +1,7 @@ # Run unit test parameters: - onnx_opsets: ['17', '16', '15', '14', '13', '12', '11', '10', '9'] + onnx_opsets: ['17', '16', '15', '14', '13'] skip_tflite_tests: 'True' skip_tfjs_tests: 'True' skip_tf_tests: 'False' diff --git a/ci_build/azure_pipelines/unit_test-matrix.yml b/ci_build/azure_pipelines/unit_test-matrix.yml index 8ff0ca47d..664d33169 100644 --- a/ci_build/azure_pipelines/unit_test-matrix.yml +++ b/ci_build/azure_pipelines/unit_test-matrix.yml @@ -7,18 +7,7 @@ stages: parameters: platforms: ['linux', 'windows'] python_versions: ['3.7'] - tf_versions: ['1.14.0'] - onnx_opsets: [''] - job: - steps: - - template: 'unit_test.yml' - report_coverage: 'True' - - - template: 'templates/job_generator.yml' - parameters: - platforms: ['linux', 'windows'] - python_versions: ['3.7'] - tf_versions: ['1.15.2','2.1.0'] + tf_versions: ['1.14.0', '1.15.2'] onnx_opsets: [''] job: steps: diff --git a/ci_build/azure_pipelines/unit_test.yml b/ci_build/azure_pipelines/unit_test.yml index 47ebf15ed..94b3e3e72 100644 --- a/ci_build/azure_pipelines/unit_test.yml +++ b/ci_build/azure_pipelines/unit_test.yml @@ -80,7 +80,7 @@ stages: - template: 'templates/job_generator.yml' parameters: # TFJS tf 2.9 - python_versions: ['3.9'] + python_versions: ['3.10'] tf_versions: ['2.9.1'] onnx_opsets: [''] skip_tfjs_tests: 'False' @@ -93,7 +93,7 @@ stages: - template: 'templates/job_generator.yml' parameters: # TFLite tf 2.9 - python_versions: ['3.8'] + python_versions: ['3.10'] tf_versions: ['2.9.1'] onnx_opsets: [''] skip_tflite_tests: 'False' @@ -162,7 +162,7 @@ stages: python_versions: ['3.9'] platforms: ['windows'] tf_versions: ['2.9.1'] - onnx_opsets: ['15'] + onnx_opsets: ['16'] job: steps: - template: 'unit_test.yml' From 3bd3081c3734dcfa1b2559dcbaaf6febdd90d215 Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Wed, 3 Aug 2022 10:42:38 +0800 Subject: [PATCH 38/40] Remove usage of numpy bool aliases for builtins Signed-off-by: Deyu Huang --- tests/test_backend.py | 38 ++++++++++++++--------------- tests/test_onnx_shape_inference.py | 4 +-- tests/test_optimizers.py | 6 ++--- tests/test_tfjs_runner.py | 2 +- tf2onnx/custom_opsets/string_ops.py | 2 +- tf2onnx/onnx_opset/nn.py | 8 +++--- tf2onnx/onnx_opset/tensor.py | 8 +++--- tf2onnx/utils.py | 2 +- 8 files changed, 35 insertions(+), 35 deletions(-) diff --git a/tests/test_backend.py b/tests/test_backend.py index cdbfeebc2..9fb6cedad 100755 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -5,7 +5,7 @@ import os import unittest -from distutils.version import LooseVersion +from packaging.version import Version from itertools import product import numpy as np @@ -72,7 +72,7 @@ matrix_diag_part = tf.compat.v1.matrix_diag_part fake_quant_with_min_max_args = tf.quantization.fake_quant_with_min_max_args fake_quant_with_min_max_vars = tf.quantization.fake_quant_with_min_max_vars -elif LooseVersion(tf.__version__) >= "1.13": +elif Version(tf.__version__) >= Version("1.13"): conv2d_backprop_input = tf.compat.v1.nn.conv2d_backprop_input conv3d_transpose = tf.compat.v1.nn.conv3d_transpose multinomial = tf.compat.v1.random.multinomial @@ -86,7 +86,7 @@ quantize_and_dequantize = tf.compat.v1.quantization.quantize_and_dequantize resize_nearest_neighbor = tf.compat.v1.image.resize_nearest_neighbor resize_bilinear = tf.compat.v1.image.resize_bilinear - if LooseVersion(tf.__version__) >= "1.14": + if Version(tf.__version__) >= Version("1.14"): resize_bilinear_v2 = tf.compat.v2.image.resize is_nan = tf.math.is_nan is_inf = tf.math.is_inf @@ -1320,8 +1320,8 @@ def func(x1): @check_onnxruntime_incompatibility("Add") def test_logicaland(self): - x_val1 = np.array([1, 0, 1, 1], dtype=np.bool).reshape((2, 2)) - x_val2 = np.array([0, 1, 1, 1], dtype=np.bool).reshape((2, 2)) + x_val1 = np.array([1, 0, 1, 1], dtype=bool).reshape((2, 2)) + x_val2 = np.array([0, 1, 1, 1], dtype=bool).reshape((2, 2)) def func(x1, x2): mi = tf.logical_and(x1, x2) return tf.identity(mi, name=_TFOUTPUT) @@ -3505,9 +3505,9 @@ def func(x): def test_where_bool(self): x_val = np.array([1, 2, -3, 4, -5], dtype=np.float32) true_result = np.array([True, False, True, False, True], - dtype=np.bool) + dtype=bool) false_result = np.array([False, True, False, True, True], - dtype=np.bool) + dtype=bool) def func(x): picks = tf.where(x > -1, true_result, false_result) return tf.identity(picks, name=_TFOUTPUT) @@ -3770,21 +3770,21 @@ def func(input_1, input_2): self._run_test_case(func, [_OUTPUT], {_INPUT: input_val_1, _INPUT1: input_val_2}, rtol=1e-4) def test_logical_not(self): - input_val = np.random.randint(0, 2, (10, 20)).astype(np.bool) + input_val = np.random.randint(0, 2, (10, 20)).astype(bool) def func(x): res = tf.logical_not(x) return tf.identity(res, name=_TFOUTPUT) self._run_test_case(func, [_OUTPUT], {_INPUT: input_val}) def test_reduce_all(self): - input_val = np.random.randint(0, 2, (2, 20)).astype(np.bool) + input_val = np.random.randint(0, 2, (2, 20)).astype(bool) def func(x): res = tf.reduce_all(input_tensor=x, keepdims=False) res1 = tf.reduce_all(input_tensor=x, axis=[0], keepdims=False) return tf.identity(res, name=_TFOUTPUT), tf.identity(res1, name=_TFOUTPUT1) self._run_test_case(func, [_OUTPUT, _OUTPUT1], {_INPUT: input_val}) - input_val = np.random.randint(0, 2, (2, 20)).astype(np.bool) + input_val = np.random.randint(0, 2, (2, 20)).astype(bool) def func(input_x): res = tf.reduce_all(input_tensor=input_x, keepdims=True) res1 = tf.reduce_all(input_tensor=input_x, axis=[0], keepdims=True) @@ -3792,14 +3792,14 @@ def func(input_x): self._run_test_case(func, [_OUTPUT, _OUTPUT1], {_INPUT: input_val}) def test_reduce_any(self): - input_val = np.random.randint(0, 2, (2, 20)).astype(np.bool) + input_val = np.random.randint(0, 2, (2, 20)).astype(bool) def func(x): res = tf.reduce_any(input_tensor=x, keepdims=False) res1 = tf.reduce_any(input_tensor=x, axis=[0], keepdims=False) return tf.identity(res, name=_TFOUTPUT), tf.identity(res1, name=_TFOUTPUT1) self._run_test_case(func, [_OUTPUT, _OUTPUT1], {_INPUT: input_val}) - input_val = np.random.randint(0, 2, (2, 20)).astype(np.bool) + input_val = np.random.randint(0, 2, (2, 20)).astype(bool) def func(x): res = tf.reduce_any(input_tensor=x, keepdims=True) res1 = tf.reduce_any(input_tensor=x, axis=[0], keepdims=True) @@ -3808,14 +3808,14 @@ def func(x): @check_opset_min_version(11, "ReduceMin") def test_reduce_all_negative_axis(self): - input_val = np.random.randint(0, 2, (2, 20)).astype(np.bool) + input_val = np.random.randint(0, 2, (2, 20)).astype(bool) def func(x): res = tf.reduce_all(input_tensor=x, keepdims=False) res1 = tf.reduce_all(input_tensor=x, axis=[-1], keepdims=False) return tf.identity(res, name=_TFOUTPUT), tf.identity(res1, name=_TFOUTPUT1) self._run_test_case(func, [_OUTPUT, _OUTPUT1], {_INPUT: input_val}) - input_val = np.random.randint(0, 2, (2, 20)).astype(np.bool) + input_val = np.random.randint(0, 2, (2, 20)).astype(bool) def func(input_x): res = tf.reduce_all(input_tensor=input_x, keepdims=True) res1 = tf.reduce_all(input_tensor=input_x, axis=[-1], keepdims=True) @@ -3824,14 +3824,14 @@ def func(input_x): @check_opset_min_version(11, "ReduceSum") def test_reduce_any_negative_axis(self): - input_val = np.random.randint(0, 2, (2, 20)).astype(np.bool) + input_val = np.random.randint(0, 2, (2, 20)).astype(bool) def func(x): res = tf.reduce_any(input_tensor=x, keepdims=False) res1 = tf.reduce_any(input_tensor=x, axis=[-1], keepdims=False) return tf.identity(res, name=_TFOUTPUT), tf.identity(res1, name=_TFOUTPUT1) self._run_test_case(func, [_OUTPUT, _OUTPUT1], {_INPUT: input_val}) - input_val = np.random.randint(0, 2, (2, 20)).astype(np.bool) + input_val = np.random.randint(0, 2, (2, 20)).astype(bool) def func(x): res = tf.reduce_any(input_tensor=x, keepdims=True) res1 = tf.reduce_any(input_tensor=x, axis=[-1], keepdims=True) @@ -3841,7 +3841,7 @@ def func(x): @check_opset_min_version(11, "ReduceSum") @check_tf_min_version("1.15") def test_reduce_any_empty_axis(self): - input_val = np.random.randint(0, 2, (2, 20)).astype(np.bool) + input_val = np.random.randint(0, 2, (2, 20)).astype(bool) def func(x): res = tf.reduce_any(input_tensor=x, keepdims=False) res1 = tf.reduce_any(input_tensor=x, axis=[], keepdims=False) @@ -3849,7 +3849,7 @@ def func(x): self._run_test_case(func, [_OUTPUT, _OUTPUT1], {_INPUT: input_val}) def test_reduce_all_scalar_axis(self): - input_val = np.random.randint(0, 2, (2, 20)).astype(np.bool) + input_val = np.random.randint(0, 2, (2, 20)).astype(bool) def func(x): res = tf.reduce_all(input_tensor=x, keepdims=False) res1 = tf.reduce_all(input_tensor=x, axis=0, keepdims=False) @@ -3859,7 +3859,7 @@ def func(x): @check_opset_min_version(13, "ReduceSum") @check_tf_min_version("1.15") def test_reduce_any_nonconst_axis(self): - input_val = np.random.randint(0, 2, (2, 20)).astype(np.bool) + input_val = np.random.randint(0, 2, (2, 20)).astype(bool) y_val = np.array([1], np.int32) def func(x, y): res = tf.reduce_any(input_tensor=x, axis=y, keepdims=False) diff --git a/tests/test_onnx_shape_inference.py b/tests/test_onnx_shape_inference.py index 6272fa2ff..244e1b6bb 100644 --- a/tests/test_onnx_shape_inference.py +++ b/tests/test_onnx_shape_inference.py @@ -353,7 +353,7 @@ def test_if(self): sub = else_subgraph.make_node("Sub", [INPUT1, INPUT3]) else_subgraph.add_graph_output(sub.output[0]) - cond = graph.make_const("cond", np.array(True, dtype=np.bool)) + cond = graph.make_const("cond", np.array(True, dtype=bool)) branches = {"then_branch": then_subgraph, "else_branch": else_subgraph} if_node = graph.make_node("If", [cond.output[0]], branches=branches) @@ -381,7 +381,7 @@ def test_loop(self): subgraph.add_graph_output(out.output[0]) max_iter = graph.make_const("max_iter", np.array([10], dtype=np.int64)) - cond_const = graph.make_const("cond_const", np.array([True], dtype=np.bool)) + cond_const = graph.make_const("cond_const", np.array([True], dtype=bool)) branches = {"body": subgraph} loop = graph.make_node("Loop", [max_iter.output[0], cond_const.output[0], INPUT1], output_count=2, branches=branches) diff --git a/tests/test_optimizers.py b/tests/test_optimizers.py index 8261d083f..2495bab58 100644 --- a/tests/test_optimizers.py +++ b/tests/test_optimizers.py @@ -988,7 +988,7 @@ def _define_loop_graph(external_inputs): def _make_loop(external_inputs, outputs): trip_cnt = self._make_onnx_const(np.array(10, dtype=np.int64), "trip_cnt") - cond = self._make_onnx_const(np.array(True, dtype=np.bool), "cond") + cond = self._make_onnx_const(np.array(True, dtype=bool), "cond") sub_graph = _define_loop_graph(external_inputs) loop_node = helper.make_node("Loop", ["trip_cnt", "cond", "cond"], outputs, name="loop", body=sub_graph) @@ -1779,7 +1779,7 @@ def test_identity_in_subgraph_non_graph_output(self): ), ) - cond_value = np.array(True, dtype=np.bool) + cond_value = np.array(True, dtype=bool) node3 = helper.make_node( 'Constant', inputs=[], @@ -1788,7 +1788,7 @@ def test_identity_in_subgraph_non_graph_output(self): name='cond_value', data_type=TensorProto.BOOL, dims=iter_num_value.shape, - vals=cond_value.flatten().astype(np.bool).tolist(), + vals=cond_value.flatten().astype(bool).tolist(), ), ) diff --git a/tests/test_tfjs_runner.py b/tests/test_tfjs_runner.py index b0a2fedba..0f282a4d2 100644 --- a/tests/test_tfjs_runner.py +++ b/tests/test_tfjs_runner.py @@ -17,7 +17,7 @@ class TestTfjsRunner(unittest.TestCase): def test_tfjs_runner(self): float_array = np.array([[1.1, 2.2], [3.3, 4.4]], np.float32) int_array = np.array([[1, 2], [3, 4]], np.int32) - bool_array = np.array([[True, False], [True, True]], np.bool) + bool_array = np.array([[True, False], [True, True]], bool) string_array = np.array([['Hello world', ''], ['π', 'Tensor']], np.str) complex_array = np.array([[1 + 0.1j, 2 + 0.2j], [3 + 0.3j, 4 + 0.4j]], np.complex64) diff --git a/tf2onnx/custom_opsets/string_ops.py b/tf2onnx/custom_opsets/string_ops.py index 31fb0e363..303fcd94b 100644 --- a/tf2onnx/custom_opsets/string_ops.py +++ b/tf2onnx/custom_opsets/string_ops.py @@ -30,7 +30,7 @@ def version_1(cls, ctx, node, **kwargs): del node.attr[a] unsqueeze_node = GraphBuilder(ctx).make_unsqueeze({'data': node.input[1], 'axes': [0]}, return_node=True) - skip_empty_const = ctx.make_const(utils.make_name('skip_empty_const'), np.array([skip_empty], np.bool)) + skip_empty_const = ctx.make_const(utils.make_name('skip_empty_const'), np.array([skip_empty], bool)) ctx.replace_inputs(node, [node.input[0], unsqueeze_node.output[0], skip_empty_const.output[0]]) @tf_op("StringToHashBucketFast", domain=constants.CONTRIB_OPS_DOMAIN) diff --git a/tf2onnx/onnx_opset/nn.py b/tf2onnx/onnx_opset/nn.py index 5ff8567d3..adde19641 100644 --- a/tf2onnx/onnx_opset/nn.py +++ b/tf2onnx/onnx_opset/nn.py @@ -853,7 +853,7 @@ def convert_symmetric_pads(cls, ctx, node): output = node.output[0] shape = ctx.make_node("Shape", [output]).output[0] dims = ctx.make_node("Split", [shape], output_count=rank).output - two_false = ctx.make_const(utils.make_name("two_false"), np.array([False, False], np.bool)).output[0] + two_false = ctx.make_const(utils.make_name("two_false"), np.array([False, False], bool)).output[0] inv_second = ctx.make_const(utils.make_name("inv_second"), np.array([1, -1], np.int64)).output[0] dec_second = ctx.make_const(utils.make_name("dec_second"), np.array([0, 1], np.int64)).output[0] for a in non_zero_axes: @@ -1325,7 +1325,7 @@ def any_version_after11(cls, opset, ctx, node, **kwargs): g.add_graph_output(cond_out_name, TensorProto.BOOL, []) g.add_graph_output(squeeze_x.output[0], ctx.get_dtype(node.input[0]), [-1, -1, -1]) trip_node = ctx.make_node("Size", [box_ind]) - cond_const = ctx.make_const(utils.make_name("cond"), np.ones((), dtype=np.bool)) + cond_const = ctx.make_const(utils.make_name("cond"), np.ones((), dtype=bool)) ctx.remove_node(node.name) branches = {"body": g} inner_loop = ctx.make_node("Loop", [trip_node.output[0], cond_const.output[0]], name=node.name, @@ -1638,7 +1638,7 @@ def version_7(cls, ctx, node, **kwargs): # 2: "loop" to generate mask matrix: generate col or row of matrix one by one g = ctx.create_new_graph_with_same_config() node_name = utils.make_name("const_zero_bool") - const_zero_bool = g.make_const(name=node_name, np_val=np.array([[0]]).astype(np.bool)) + const_zero_bool = g.make_const(name=node_name, np_val=np.array([[0]]).astype(bool)) g.set_dtype(const_zero_bool.output[0], onnx_pb.TensorProto.BOOL) g.add_graph_input("trip", onnx_pb.TensorProto.INT64, []) @@ -1668,7 +1668,7 @@ def version_7(cls, ctx, node, **kwargs): line_num = ctx.make_node(op_type="Gather", inputs=[shape.output[0], col_or_row_num_index.output[0]]) trip_cnt = line_num.output[0] node_name = utils.make_name("true") - cond = ctx.make_const(name=node_name, np_val=np.array(1).astype(np.bool)) + cond = ctx.make_const(name=node_name, np_val=np.array(1).astype(bool)) col_init = one_line.output[0] branches = {"body": g} diff --git a/tf2onnx/onnx_opset/tensor.py b/tf2onnx/onnx_opset/tensor.py index b08188b88..044a253d4 100644 --- a/tf2onnx/onnx_opset/tensor.py +++ b/tf2onnx/onnx_opset/tensor.py @@ -497,7 +497,7 @@ def _make_gathernd_inner_loop(ctx, params, index, dtype): # gather_res = gather(gather_cur, index[i]) scope_name = utils.make_name("gathernd_inner_loop") trip_node = ctx.make_node("Size", [index.output[0]]) - cond_const = ctx.make_const(utils.make_name("cond"), np.ones((), dtype=np.bool)) + cond_const = ctx.make_const(utils.make_name("cond"), np.ones((), dtype=bool)) trip_name = utils.make_name("i") cond_name = utils.make_name("cond") cond_out_name = utils.make_name("cond_out") @@ -548,7 +548,7 @@ def make_gathernd(ctx, params, indices, output, scope_name, t_params, shapes, dt # outter loop for each index # for (int i=0; i Date: Wed, 3 Aug 2022 10:45:42 +0800 Subject: [PATCH 39/40] Use packaging library to avoid DeprecationWarning from distutils Signed-off-by: Deyu Huang --- tests/common.py | 16 ++++++++-------- tests/run_pretrained_models.py | 4 ++-- tests/test_backend.py | 2 +- tf2onnx/convert.py | 8 ++++---- tf2onnx/shape_inference.py | 4 ++-- tf2onnx/tf_loader.py | 14 +++++++------- tf2onnx/tf_utils.py | 4 ++-- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/common.py b/tests/common.py index f92fa1f51..82ca09c49 100644 --- a/tests/common.py +++ b/tests/common.py @@ -9,7 +9,7 @@ import unittest from collections import defaultdict -from distutils.version import LooseVersion +from packaging.version import Version from parameterized import parameterized import numpy as np import tensorflow as tf @@ -98,7 +98,7 @@ def _get_backend_version(self): pass if version: - version = LooseVersion(version) + version = Version(version) return version def __str__(self): @@ -178,7 +178,7 @@ def check_opset_after_tf_version(tf_version, required_opset, message=""): """ Skip if tf_version > max_required_version """ config = get_test_config() reason = _append_message("conversion requires opset {} after tf {}".format(required_opset, tf_version), message) - skip = config.tf_version >= LooseVersion(tf_version) and config.opset < required_opset + skip = config.tf_version >= Version(tf_version) and config.opset < required_opset return unittest.skipIf(skip, reason) @@ -284,7 +284,7 @@ def check_tfjs_max_version(max_accepted_version, message=""): except ModuleNotFoundError: can_import = False return unittest.skipIf(can_import and not config.skip_tfjs_tests and \ - tensorflowjs.__version__ > LooseVersion(max_accepted_version), reason) + Version(tensorflowjs.__version__) > Version(max_accepted_version), reason) def check_tfjs_min_version(min_required_version, message=""): """ Skip if tjs_version < min_required_version """ @@ -296,20 +296,20 @@ def check_tfjs_min_version(min_required_version, message=""): except ModuleNotFoundError: can_import = False return unittest.skipIf(can_import and not config.skip_tfjs_tests and \ - tensorflowjs.__version__ < LooseVersion(min_required_version), reason) + Version(tensorflowjs.__version__) < Version(min_required_version), reason) def check_tf_max_version(max_accepted_version, message=""): """ Skip if tf_version > max_required_version """ config = get_test_config() reason = _append_message("conversion requires tf <= {}".format(max_accepted_version), message) - return unittest.skipIf(config.tf_version > LooseVersion(max_accepted_version), reason) + return unittest.skipIf(config.tf_version > Version(max_accepted_version), reason) def check_tf_min_version(min_required_version, message=""): """ Skip if tf_version < min_required_version """ config = get_test_config() reason = _append_message("conversion requires tf >= {}".format(min_required_version), message) - return unittest.skipIf(config.tf_version < LooseVersion(min_required_version), reason) + return unittest.skipIf(config.tf_version < Version(min_required_version), reason) def skip_tf_versions(excluded_versions, message=""): @@ -385,7 +385,7 @@ def check_onnxruntime_min_version(min_required_version, message=""): config = get_test_config() reason = _append_message("conversion requires onnxruntime >= {}".format(min_required_version), message) return unittest.skipIf(config.is_onnxruntime_backend and - config.backend_version < LooseVersion(min_required_version), reason) + config.backend_version < Version(min_required_version), reason) def skip_caffe2_backend(message=""): diff --git a/tests/run_pretrained_models.py b/tests/run_pretrained_models.py index 78f36a79c..626cba0d5 100644 --- a/tests/run_pretrained_models.py +++ b/tests/run_pretrained_models.py @@ -17,7 +17,7 @@ import zipfile import random from collections import namedtuple -from distutils.version import LooseVersion +from packaging.version import Version import yaml @@ -789,7 +789,7 @@ def main(): continue if t.tf_min_version: - if tf_utils.get_tf_version() < LooseVersion(str(t.tf_min_version)): + if tf_utils.get_tf_version() < Version(str(t.tf_min_version)): logger.info("Skip %s: %s %s", test, "Min TF version needed:", t.tf_min_version) continue diff --git a/tests/test_backend.py b/tests/test_backend.py index 9fb6cedad..8876da855 100755 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -5,11 +5,11 @@ import os import unittest -from packaging.version import Version from itertools import product import numpy as np from numpy.testing import assert_almost_equal +from packaging.version import Version import tensorflow as tf from tensorflow.python.ops import lookup_ops diff --git a/tf2onnx/convert.py b/tf2onnx/convert.py index 32c28f0bc..02a78e439 100644 --- a/tf2onnx/convert.py +++ b/tf2onnx/convert.py @@ -10,7 +10,7 @@ import argparse import os import sys -from distutils.version import LooseVersion +from packaging.version import Version os.environ['TF_CPP_MIN_LOG_LEVEL'] = "3" @@ -20,7 +20,7 @@ from tf2onnx import constants, logging, utils, optimizer from tf2onnx import tf_loader from tf2onnx.graph import ExternalTensorStorage -from tf2onnx.tf_utils import compress_graph_def +from tf2onnx.tf_utils import compress_graph_def, get_tf_version @@ -431,7 +431,7 @@ def from_keras(model, input_signature=None, opset=None, custom_ops=None, custom_ Returns: An ONNX model_proto and an external_tensor_storage dict. """ - if LooseVersion(tf.__version__) < "2.0": + if get_tf_version() < Version("2.0"): return _from_keras_tf1(model, opset, custom_ops, custom_op_handlers, custom_rewriter, inputs_as_nchw, outputs_as_nchw, extra_opset, shape_override, target, large_model, output_path) @@ -540,7 +540,7 @@ def from_function(function, input_signature=None, opset=None, custom_ops=None, c Returns: An ONNX model_proto and an external_tensor_storage dict. """ - if LooseVersion(tf.__version__) < "2.0": + if get_tf_version() < Version("2.0"): raise NotImplementedError("from_function requires tf-2.0 or newer") if input_signature is None: diff --git a/tf2onnx/shape_inference.py b/tf2onnx/shape_inference.py index 9a28975b5..853d4835c 100644 --- a/tf2onnx/shape_inference.py +++ b/tf2onnx/shape_inference.py @@ -6,9 +6,9 @@ """ import logging -from distutils.version import LooseVersion from collections import defaultdict import numpy as np +from packaging.version import Version from tf2onnx import utils from tf2onnx.tf_utils import get_tf_tensor_shape, get_tf_const_value, get_tf_shape_attr, get_tf_version from tf2onnx.tf_loader import tf_reload_graph @@ -32,7 +32,7 @@ def infer_shape(tf_graph, shape_override): op_outputs_with_none_shape = check_shape_for_tf_graph(tf_graph) if op_outputs_with_none_shape: - if get_tf_version() > LooseVersion("1.5.0"): + if get_tf_version() > Version("1.5.0"): for op, outs in op_outputs_with_none_shape.items(): logger.warning( "Cannot infer shape for %s: %s", diff --git a/tf2onnx/tf_loader.py b/tf2onnx/tf_loader.py index 22d909b4f..7269a26e0 100644 --- a/tf2onnx/tf_loader.py +++ b/tf2onnx/tf_loader.py @@ -5,7 +5,7 @@ import logging import uuid -from distutils.version import LooseVersion +from packaging.version import Version import tensorflow as tf import numpy as np @@ -75,7 +75,7 @@ def not_implemented_tf_placeholder(*args, **kwargs): tf_placeholder = tf.compat.v1.placeholder tf_placeholder_with_default = tf.compat.v1.placeholder_with_default extract_sub_graph = tf.compat.v1.graph_util.extract_sub_graph -elif LooseVersion(tf.__version__) >= "1.13": +elif Version(tf.__version__) >= Version("1.13"): # 1.13 introduced the compat namespace tf_reset_default_graph = tf.compat.v1.reset_default_graph tf_global_variables = tf.compat.v1.global_variables @@ -162,7 +162,7 @@ def make_tensor_proto_wrapped(values, dtype=None, shape=None, verify_shape=False try: function_converter = _FunctionConverterData - if LooseVersion(tf.__version__) >= "2.6.0": + if Version(tf.__version__) >= Version("2.6.0"): from tensorflow.python.eager import context from tensorflow.python.framework.convert_to_constants import _FunctionConverterDataInEager, \ _FunctionConverterDataInGraph @@ -267,7 +267,7 @@ def from_function(func, input_names, output_names, large_model=False): return convert_variables_to_constants_large_model(func) try: - if get_tf_version() < LooseVersion("2.2"): + if get_tf_version() < Version("2.2"): frozen_func = convert_variables_to_constants_v2(func, lower_control_flow=False) else: frozen_func = convert_variables_to_constants_v2(func, lower_control_flow=False, aggressive_inlining=True) @@ -687,7 +687,7 @@ def tf_optimize_grappler(input_names, output_names, graph_def): 'constfold', 'function' ] - if LooseVersion(tf.__version__) >= "2.5": + if Version(tf.__version__) >= Version("2.5"): # This flag disables folding QDQ nodes around constants in the network (eg: around conv/FC weights) rewrite_options.experimental_disable_folding_quantization_emulation = True @@ -710,7 +710,7 @@ def tf_optimize(input_names, output_names, graph_def): [utils.node_name(i) for i in output_names] graph_def = extract_sub_graph(graph_def, needed_names) - want_grappler = is_tf2() or LooseVersion(tf.__version__) >= "1.15" + want_grappler = is_tf2() or Version(tf.__version__) >= Version("1.15") if want_grappler: graph_def = tf_optimize_grappler(input_names, output_names, graph_def) else: @@ -730,7 +730,7 @@ def tf_optimize(input_names, output_names, graph_def): def tf_reload_graph(tf_graph): """Invoke tensorflow cpp shape inference by reloading graph_def.""" # invoke c api if tf version is below 1.8 - if get_tf_version() < LooseVersion("1.8"): + if get_tf_version() < Version("1.8"): logger.debug( "On TF < 1.8, graph is constructed by python API, " "which doesn't invoke shape inference, please set " diff --git a/tf2onnx/tf_utils.py b/tf2onnx/tf_utils.py index 54abc7071..1f59adae4 100644 --- a/tf2onnx/tf_utils.py +++ b/tf2onnx/tf_utils.py @@ -6,7 +6,7 @@ """ import collections -from distutils.version import LooseVersion +from packaging.version import Version import numpy as np import tensorflow as tf @@ -121,7 +121,7 @@ def get_tf_node_attr(node, name): def get_tf_version(): - return LooseVersion(tf.__version__) + return Version(tf.__version__) def compress_graph_def(graph_def): """ From 4debef7a9adb3991071d6f79813f710bed314421 Mon Sep 17 00:00:00 2001 From: Deyu Huang Date: Fri, 12 Aug 2022 10:43:45 +0800 Subject: [PATCH 40/40] Turn on graph tf optimize grappler dependency (#2020) * Turn on graph tf optimize grappler dependency Signed-off-by: Deyu Huang * Aviod output name rewrite in random normal Signed-off-by: Deyu Huang Co-authored-by: Jay Zhang --- tf2onnx/rewriter/random_normal_rewriter.py | 16 +++++++++++----- tf2onnx/tf_loader.py | 8 ++++++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/tf2onnx/rewriter/random_normal_rewriter.py b/tf2onnx/rewriter/random_normal_rewriter.py index 6d106907e..3691b5f00 100644 --- a/tf2onnx/rewriter/random_normal_rewriter.py +++ b/tf2onnx/rewriter/random_normal_rewriter.py @@ -33,16 +33,22 @@ def rewrite_random_normal(g, ops): match_results = list(matcher.match_ops(ops)) for match in match_results: output = match.get_op('output') - if output.type == 'Add': + input2 = match.get_op('input2') + is_output = False + for output_name in g.outputs: + # input2 and output can not be output node. + if input2.name in output_name or output.name in output_name: + is_output = True + break + if is_output: + continue + if output.type == 'Add' and input2.type == 'Mul': # pattern 1 mean = output.inputs[1].get_tensor_value() + scale = input2.inputs[1].get_tensor_value() else: # pattern 2 mean = 0.0 - input2 = match.get_op('input2') - if input2.type == 'Mul': - scale = input2.inputs[1].get_tensor_value() - else: scale = 1.0 dtype = g.get_dtype(output.output[0]) op_name = utils.make_name("RandomNormal") diff --git a/tf2onnx/tf_loader.py b/tf2onnx/tf_loader.py index 7269a26e0..d9d72a8dc 100644 --- a/tf2onnx/tf_loader.py +++ b/tf2onnx/tf_loader.py @@ -687,6 +687,10 @@ def tf_optimize_grappler(input_names, output_names, graph_def): 'constfold', 'function' ] + if is_tf2(): + # add for tf2.x lstm optimization. + rewrite_options.optimizers.append('dependency') + if Version(tf.__version__) >= Version("2.5"): # This flag disables folding QDQ nodes around constants in the network (eg: around conv/FC weights) rewrite_options.experimental_disable_folding_quantization_emulation = True @@ -771,8 +775,8 @@ def toposort(data): try: func = function_def_to_graph(fdef, input_shapes=input_shapes) except: # pylint: disable=bare-except - # if there is a missmatch between caller and function use the functions shape - logger.warning("shape missmatch between caller and function: %s", k) + # if there is a mismatch between caller and function use the functions shape + logger.warning("shape mismatch between caller and function: %s", k) func = function_def_to_graph(fdef) _FUNCTIONS[k] = func _, _, _, _, _, tfunctions = tflist_to_onnx(func, {})