Skip to content

Commit 45dcb15

Browse files
committed
Merge branch 'master' of https://github.com/onnx/tensorflow-onnx into bench
2 parents 9d802b1 + 9590a8d commit 45dcb15

File tree

14 files changed

+198
-27
lines changed

14 files changed

+198
-27
lines changed

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ The common issues we run into we try to document here [Troubleshooting Guide](Tr
1616

1717
<br/>
1818

19-
| Build Type | OS | Python | Tensorflow | Onnx opset | Status |
19+
| Build Type | OS | Python | Tensorflow | ONNX opset | Status |
2020
| --- | --- | --- | --- | --- | --- |
21-
| Unit Test - Basic | Linux, MacOS<sup>\*</sup>, Windows<sup>\*</sup> | 3.6, 3.7, 3.8 | 1.12-1.15, 2.1-2.5 | 7-13 | [![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) |
22-
| Unit Test - Full | Linux, MacOS, Windows | 3.6, 3.7, 3.8 | 1.12-1.15, 2.1-2.5 | 7-13 | [![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) | |
21+
| Unit Test - Basic | Linux, MacOS<sup>\*</sup>, Windows<sup>\*</sup> | 3.6-3.9 | 1.12-1.15, 2.1-2.5 | 8-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) |
22+
| Unit Test - Full | Linux, MacOS, Windows | 3.6-3.9 | 1.12-1.15, 2.1-2.5 | 8-14 | [![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) | |
2323
<br/>
2424

2525
## Supported Versions
@@ -28,21 +28,23 @@ The common issues we run into we try to document here [Troubleshooting Guide](Tr
2828

2929
tf2onnx will use the ONNX version installed on your system and installs the latest ONNX version if none is found.
3030

31-
We support ONNX opset-6 to opset-13. By default we use ```opset-9``` for the resulting ONNX graph since most runtimes will support opset-9.
31+
We support and test ONNX opset-8 to opset-14. opset-6 and opset-7 should work but we don't test them.
32+
By default we use ```opset-9``` for the resulting ONNX graph since most runtimes will support opset-9.
3233

3334
If you want the graph to be generated with a specific opset, use ```--opset``` in the command line, for example ```--opset 13```.
3435

3536
### TensorFlow
3637

37-
We support ```tf-1.x graphs``` and ```tf-2```. To keep our test matrix manageable we test tf2onnx running on top of ```tf-1.12 and up```.
38+
We support ```tf-1.x graphs``` and ```tf-2```. To keep our test matrix manageable we test tf2onnx running on top of ```tf-1.12 or better```.
3839

3940
When running under tf-2.x tf2onnx will use the tensorflow V2 controlflow.
4041

4142
You can install tf2onnx on top of tf-1.x or tf-2.x.
4243

4344
### Python
4445

45-
We support Python ```3.6```, ```3.7``` and ```3.8```.
46+
We support Python ```3.6-3.9```.
47+
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.
4648

4749
## Prerequisites
4850

ci_build/azure_pipelines/pretrained_model_test-matrix.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
parameters:
3333
platforms: ['linux', 'windows']
3434
python_versions: ['3.8']
35-
tf_versions: ['2.5.0rc1']
35+
tf_versions: ['2.5.0']
3636
job:
3737
steps:
3838
- template: 'pretrained_model_test.yml'

ci_build/azure_pipelines/templates/job_generator.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ parameters:
55
python_versions: ['3.7']
66
tf_versions: ['']
77
onnx_versions: ['']
8-
onnx_opsets: ['13', '12', '11', '10', '9', '8', '7']
8+
onnx_opsets: ['14', '13', '12', '11', '10', '9', '8']
99
onnx_backends: {onnxruntime: ['1.8.0']}
1010
job: {}
1111
run_setup: 'True'

ci_build/azure_pipelines/templates/unit_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Run unit test
22

33
parameters:
4-
onnx_opsets: ['13', '12', '11', '10', '9', '8', '7']
4+
onnx_opsets: ['14', '13', '12', '11', '10', '9', '8']
55
skip_tflite_tests: 'True'
66
skip_tf_tests: 'False'
77

ci_build/azure_pipelines/unit_test-matrix.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ stages:
4040
parameters:
4141
platforms: ['linux', 'windows']
4242
python_versions: ['3.8']
43-
tf_versions: ['2.5.0rc1']
43+
tf_versions: ['2.5.0']
4444
onnx_opsets: ['']
4545
job:
4646
steps:

ci_build/azure_pipelines/unit_test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ stages:
66
- template: 'templates/job_generator.yml'
77
parameters:
88
python_versions: ['3.8']
9-
tf_versions: ['2.5.0rc1']
9+
tf_versions: ['2.5.0']
1010
onnx_opsets: ['']
1111
skip_tflite_tests: 'False'
1212
skip_tf_tests: 'True'
@@ -30,7 +30,7 @@ stages:
3030
- template: 'templates/job_generator.yml'
3131
parameters:
3232
python_versions: ['3.8']
33-
tf_versions: ['2.5.0rc1']
33+
tf_versions: ['2.5.0']
3434
onnx_opsets: ['']
3535
job:
3636
steps:

tf2onnx/constants.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
TARGET_RS6 = "rs6"
3030
TARGET_CAFFE2 = "caffe2"
3131
TARGET_TENSORRT = "tensorrt"
32-
POSSIBLE_TARGETS = [TARGET_RS4, TARGET_RS5, TARGET_RS6, TARGET_CAFFE2, TARGET_TENSORRT]
32+
TARGET_CHANNELS_LAST = "nhwc"
33+
TARGET_CHANNELS_FIRST = "nchw"
34+
POSSIBLE_TARGETS = [TARGET_RS4, TARGET_RS5, TARGET_RS6, TARGET_CAFFE2, TARGET_TENSORRT, TARGET_CHANNELS_LAST]
3335
DEFAULT_TARGET = []
3436

3537
NCHW_TO_NHWC = [0, 2, 3, 1]
@@ -45,5 +47,5 @@
4547
# Mapping opset to IR version.
4648
# Note: opset 7 and opset 8 came out with IR3 but we need IR4 because of PlaceholderWithDefault
4749
OPSET_TO_IR_VERSION = {
48-
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
50+
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
4951
}

tf2onnx/graph.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,13 +1124,29 @@ def make_graph(self, doc, graph_name=None, external_tensor_storage=None):
11241124
# create output_tensor_values
11251125
output_tensor_values = self.make_onnx_graph_io(self.outputs)
11261126

1127+
tensor_value_info = []
1128+
1129+
for op in ops:
1130+
if op.domain in [constants.ONNX_DOMAIN, constants.AI_ONNX_ML_DOMAIN]:
1131+
continue
1132+
# We still don't 100% trust the accuracy of all the shapes in graph.py, but for custom ops they are
1133+
# almost certainly accurate and onnx has no other way of knowing them.
1134+
for out in op.output:
1135+
if out == '' or out in self.outputs:
1136+
continue
1137+
dtype = self.get_dtype(out)
1138+
shape = self.get_shape(out)
1139+
v = utils.make_onnx_inputs_outputs(out, dtype, shape)
1140+
tensor_value_info.append(v)
1141+
11271142
# create graph proto
11281143
graph = helper.make_graph([op.op for op in ops],
11291144
graph_name,
11301145
input_tensor_values,
11311146
output_tensor_values,
11321147
initializer=initializers,
1133-
doc_string=doc)
1148+
doc_string=doc,
1149+
value_info=tensor_value_info)
11341150

11351151
return graph
11361152

@@ -1628,10 +1644,11 @@ def get_onnx_model_properties(onnx_model_proto):
16281644
return kwargs
16291645

16301646
@staticmethod
1631-
def create_graph_from_onnx_model(onnx_model_proto):
1647+
def create_graph_from_onnx_model(onnx_model_proto, target=None):
16321648
"""Create Graph loading onnx model proto."""
16331649
# apply shape inference on the model
16341650
inferred_model = shape_inference.infer_shapes(onnx_model_proto)
1651+
utils.initialize_name_counter(inferred_model)
16351652
graph_proto = inferred_model.graph
16361653

16371654
opset_version = None
@@ -1644,11 +1661,11 @@ def create_graph_from_onnx_model(onnx_model_proto):
16441661
extra_opset.append(opset)
16451662

16461663
utils.make_sure(opset_version is not None, "opset version is not specified for onnx domain")
1647-
main_graph = GraphUtil.create_graph_from_onnx_graph(graph_proto, opset_version, extra_opset)
1664+
main_graph = GraphUtil.create_graph_from_onnx_graph(graph_proto, opset_version, extra_opset, target)
16481665
return main_graph
16491666

16501667
@staticmethod
1651-
def create_graph_from_onnx_graph(graph_proto, opset_version=None, extra_opset=None):
1668+
def create_graph_from_onnx_graph(graph_proto, opset_version=None, extra_opset=None, target=None):
16521669
"""Create Graph loading onnx graph proto."""
16531670
output_shapes = {}
16541671
output_dtypes = {}
@@ -1675,7 +1692,7 @@ def create_graph_from_onnx_graph(graph_proto, opset_version=None, extra_opset=No
16751692
for n in graph_proto.output:
16761693
output_names.append(n.name)
16771694

1678-
g = Graph(nodes_to_append, output_shapes, output_dtypes, None, opset_version, extra_opset, None, output_names)
1695+
g = Graph(nodes_to_append, output_shapes, output_dtypes, target, opset_version, extra_opset, None, output_names)
16791696
const_nodes = GraphUtil._parse_graph_initializer(g, graph_proto)
16801697
GraphUtil._parse_graph_input(g, graph_proto, [n.name for n in const_nodes])
16811698

@@ -1702,6 +1719,10 @@ def _parse_shape_and_type_from_value_infos(value_infos):
17021719
for shape_info in value_infos:
17031720
type_proto = shape_info.type
17041721
elem_type = type_proto.tensor_type.elem_type
1722+
output_dtypes[shape_info.name] = elem_type
1723+
if not type_proto.tensor_type.HasField("shape"):
1724+
output_shapes[shape_info.name] = None
1725+
continue
17051726
shape = type_proto.tensor_type.shape
17061727
tuned_shape = []
17071728
for d in shape.dim:
@@ -1713,7 +1734,6 @@ def _parse_shape_and_type_from_value_infos(value_infos):
17131734
# it is found, some unknown dims is missing after inference.
17141735
tuned_shape.append(-1)
17151736
output_shapes[shape_info.name] = tuned_shape
1716-
output_dtypes[shape_info.name] = elem_type
17171737

17181738
return output_shapes, output_dtypes
17191739

tf2onnx/late_rewriters/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
"""tf2onnx.late_rewriters module."""
4+
5+
from tf2onnx.late_rewriters.channel_order_rewriters import rewrite_channels_first, rewrite_channels_last
6+
7+
8+
__all__ = [
9+
"rewrite_channels_first",
10+
"rewrite_channels_last",
11+
]
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
4+
"""
5+
tf2onnx.late_rewriters.channel_order_rewriters - contains rewriters for replacing ops with channel first/last versions
6+
"""
7+
8+
from tf2onnx import utils, constants
9+
10+
# pylint: disable=invalid-name,unused-argument,missing-docstring, unused-variable
11+
12+
_CHANNELS_FIRST_OPS = [
13+
"AveragePool",
14+
"BatchNormalization",
15+
"Conv",
16+
"ConvInteger",
17+
"ConvTranspose",
18+
"GlobalAveragePool",
19+
"GlobalLpPool",
20+
"GlobalMaxPool",
21+
"InstanceNormalization",
22+
"LpPool",
23+
"LRN",
24+
"MaxPool",
25+
"MaxRoiPool",
26+
"MaxUnpool",
27+
"QLinearConv",
28+
]
29+
30+
31+
def channel_last_to_first_perm(rank):
32+
return [0, rank - 1] + list(range(1, rank - 1))
33+
34+
35+
def channel_first_to_last_perm(rank):
36+
return [0] + list(range(2, rank)) + [1]
37+
38+
39+
def _to_channel_last_handler(g, op):
40+
# For now, all ops can use the same handlers (input[0] and output[0] are always correct)
41+
rank = g.get_rank(op.output[0])
42+
utils.make_sure(rank is not None, "Cannot convert %s node %s with unknown rank to channels last", op.type, op.name)
43+
op.type = "ChannelsLast" + op.type
44+
op.domain = constants.CONTRIB_OPS_DOMAIN
45+
inp_perm = channel_first_to_last_perm(rank)
46+
out_perm = channel_last_to_first_perm(rank)
47+
output_shape = g.get_shape(op.output[0])
48+
if output_shape is not None:
49+
output_shape = [output_shape[i] for i in inp_perm]
50+
g.set_shape(op.output[0], output_shape)
51+
52+
g.insert_new_node_on_input(op, "Transpose", op.input[0], input_index=0, perm=inp_perm)
53+
g.insert_new_node_on_output("Transpose", op.output[0], perm=out_perm)
54+
55+
56+
def _to_channel_first_handler(g, op):
57+
rank = g.get_rank(op.output[0])
58+
utils.make_sure(rank is not None, "Cannot convert %s node %s with unknown rank to channels last", op.type, op.name)
59+
op.type = op.type.replace("ChannelsLast", "")
60+
op.domain = constants.ONNX_DOMAIN
61+
inp_perm = channel_last_to_first_perm(rank)
62+
out_perm = channel_first_to_last_perm(rank)
63+
output_shape = g.get_shape(op.output[0])
64+
if output_shape is not None:
65+
output_shape = [output_shape[i] for i in inp_perm]
66+
g.set_shape(op.output[0], output_shape)
67+
68+
g.insert_new_node_on_input(op, "Transpose", op.input[0], input_index=0, perm=inp_perm)
69+
g.insert_new_node_on_output("Transpose", op.output[0], perm=out_perm)
70+
71+
72+
def get_channels_first_ops(opset=None):
73+
# opset doesn't matter for now
74+
return set(_CHANNELS_FIRST_OPS)
75+
76+
77+
def rewrite_channels_last(g, ops):
78+
channel_first_ops = get_channels_first_ops(g.opset)
79+
for op in ops:
80+
if op.type in channel_first_ops:
81+
_to_channel_last_handler(g, op)
82+
return g.get_nodes()
83+
84+
85+
def rewrite_channels_first(g, ops):
86+
for op in ops:
87+
if op.type.startswith("ChannelsLast"):
88+
_to_channel_first_handler(g, op)
89+
return g.get_nodes()

0 commit comments

Comments
 (0)