9
9
10
10
import numpy as np
11
11
from onnx import helper , TensorProto
12
+ from tf2onnx import utils
12
13
from tf2onnx .graph import GraphUtil
13
14
from backend_test_base import Tf2OnnxBackendTestBase
14
15
from common import unittest_main
19
20
class OptimizerTests (Tf2OnnxBackendTestBase ):
20
21
"""Run original model proto and modified model proto with onnxruntime, compare the results."""
21
22
22
- def run_and_compare (self , output_names_with_port , onnx_feed_dict , origin_proto ,
23
- remaining_transpose_num = None , debug = False , rtol = 1e-07 ):
23
+ def run_and_compare (self , output_names_with_port , onnx_feed_dict , origin_proto , op_type ,
24
+ remaining_op_num , debug = False , rtol = 1e-07 ):
25
+ utils .make_sure (op_type is not None , "op_type should be specified" )
26
+ utils .make_sure (remaining_op_num is not None , "remaining_op_num should be specified" )
27
+
24
28
origin_model_path = self .save_onnx_model (origin_proto , onnx_feed_dict , postfix = "_origin" )
25
29
26
- new_proto = GraphUtil .opt_transposes_with_model_proto (origin_proto )
30
+ new_proto = GraphUtil .optimize_graph_with_model_proto (origin_proto )
27
31
28
32
self .assertTrue (new_proto , msg = "model proto after optimizer should not be None" )
29
33
30
34
new_model_path = self .save_onnx_model (new_proto , onnx_feed_dict , postfix = "_opt" )
31
-
32
- previous = GraphUtil .get_node_count_from_onnx_graph (origin_proto .graph )
33
35
current = GraphUtil .get_node_count_from_onnx_graph (new_proto .graph )
34
36
35
- self .assertTrue (current ["Transpose" ] < previous [ "Transpose" ], msg = "transpose ops count not changed" )
36
- if remaining_transpose_num is not None :
37
- self . assertTrue (current ["Transpose" ] == remaining_transpose_num , msg = "some transpose ops left unexpected " )
37
+ self .assertTrue (current [op_type ] == remaining_op_num ,
38
+ msg = "Expect " + str ( remaining_op_num ) + " " + op_type + " ops left, but actually " +
39
+ str (current [op_type ]) + " left" )
38
40
39
41
if self .config .is_onnxruntime_backend :
40
42
expected = self .run_onnxruntime (origin_model_path , onnx_feed_dict , output_names_with_port )
@@ -47,7 +49,14 @@ def run_and_compare(self, output_names_with_port, onnx_feed_dict, origin_proto,
47
49
self .assertEqual (expected_val .dtype , actual_val .dtype )
48
50
self .assertEqual (expected_val .shape , actual_val .shape )
49
51
50
- def test_relu (self ):
52
+ # Tranpose Optimizer Tests Start
53
+
54
+ def run_transpose_compare (self , output_names_with_port , onnx_feed_dict , origin_proto ,
55
+ remaining_transpose_num = None , debug = False , rtol = 1e-07 ):
56
+ self .run_and_compare (output_names_with_port , onnx_feed_dict , origin_proto , op_type = "Transpose" ,
57
+ remaining_op_num = remaining_transpose_num , debug = debug , rtol = rtol )
58
+
59
+ def test_transpose_relu (self ):
51
60
node1 = helper .make_node ("Transpose" , ["X" ], ["Y" ], perm = [0 , 2 , 3 , 1 ], name = "trans_1" )
52
61
node2 = helper .make_node ("Relu" , ["Y" ], ["Z" ], name = "relu" )
53
62
node3 = helper .make_node ("Transpose" , ["Z" ], ["Z1" ], perm = [0 , 3 , 1 , 2 ], name = "trans_2" )
@@ -60,10 +69,10 @@ def test_relu(self):
60
69
)
61
70
62
71
model_proto = helper .make_model (graph , producer_name = "onnx-tests" )
63
- self .run_and_compare (["Z1" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
64
- model_proto , remaining_transpose_num = 0 )
72
+ self .run_transpose_compare (["Z1" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
73
+ model_proto , remaining_transpose_num = 0 )
65
74
66
- def test_leaky_relu (self ):
75
+ def test_transpose_leaky_relu (self ):
67
76
node1 = helper .make_node ("Transpose" , ["X" ], ["Y" ], perm = [0 , 2 , 3 , 1 ], name = "trans_1" )
68
77
node2 = helper .make_node ("LeakyRelu" , ["Y" ], ["Z" ], alpha = 0.02 , name = "relu" )
69
78
node3 = helper .make_node ("Transpose" , ["Z" ], ["Z1" ], perm = [0 , 3 , 1 , 2 ], name = "trans_2" )
@@ -76,10 +85,10 @@ def test_leaky_relu(self):
76
85
)
77
86
78
87
model_proto = helper .make_model (graph , producer_name = "onnx-tests" )
79
- self .run_and_compare (["Z1" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
80
- model_proto , remaining_transpose_num = 0 )
88
+ self .run_transpose_compare (["Z1" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
89
+ model_proto , remaining_transpose_num = 0 )
81
90
82
- def test_max (self ):
91
+ def test_transpose_max (self ):
83
92
const_1_val = [2.0 ]
84
93
const_1 = helper .make_tensor ("const_1" , TensorProto .FLOAT , (1 ,), const_1_val )
85
94
const_1_node = helper .make_node ("Constant" , [], ["const_1" ], value = const_1 , name = "const_1" )
@@ -104,8 +113,8 @@ def test_max(self):
104
113
)
105
114
106
115
model_proto = helper .make_model (graph , producer_name = "onnx-tests" )
107
- self .run_and_compare (["Z1" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
108
- model_proto , remaining_transpose_num = 0 )
116
+ self .run_transpose_compare (["Z1" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
117
+ model_proto , remaining_transpose_num = 0 )
109
118
110
119
def test_transpose_merge (self ):
111
120
node0 = helper .make_node ("Transpose" , ["X" ], ["Y" ], perm = [0 , 2 , 3 , 1 ], name = "trans" )
@@ -120,8 +129,8 @@ def test_transpose_merge(self):
120
129
)
121
130
122
131
model_proto = helper .make_model (graph , producer_name = "onnx-tests" )
123
- self .run_and_compare (["OUT" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
124
- model_proto , remaining_transpose_num = 1 )
132
+ self .run_transpose_compare (["OUT" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
133
+ model_proto , remaining_transpose_num = 1 )
125
134
126
135
def test_transpose_with_shape (self ):
127
136
node1 = helper .make_node ("Transpose" , ["X" ], ["Y" ], perm = [0 , 2 , 3 , 1 ], name = "trans" )
@@ -135,9 +144,135 @@ def test_transpose_with_shape(self):
135
144
)
136
145
137
146
model_proto = helper .make_model (graph , producer_name = "onnx-tests" )
138
- self .run_and_compare (["Z" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
139
- model_proto , remaining_transpose_num = 0 )
147
+ self .run_transpose_compare (["Z" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
148
+ model_proto , remaining_transpose_num = 0 )
149
+
150
+ # Tranpose Optimizer Tests End
151
+
152
+ # Identity Optimizer Tests Start
153
+
154
+ def run_identity_compare (self , output_names_with_port , onnx_feed_dict , origin_proto ,
155
+ remaining_identity_num = None , debug = False , rtol = 1e-07 ):
156
+ self .run_and_compare (output_names_with_port , onnx_feed_dict , origin_proto , op_type = "Identity" ,
157
+ remaining_op_num = remaining_identity_num , debug = debug , rtol = rtol )
158
+
159
+ def test_identity_non_graph_output (self ):
160
+ node1 = helper .make_node ("Add" , ["X" , "X" ], ["Y" ], name = "add" )
161
+ node2 = helper .make_node ("Identity" , ["Y" ], ["Z" ], name = "identity" )
162
+ node3 = helper .make_node ("Shape" , ["Z" ], ["Z1" ], name = "shape" )
163
+
164
+ graph = helper .make_graph (
165
+ [node1 , node2 , node3 ],
166
+ "identity-test" ,
167
+ [helper .make_tensor_value_info ("X" , TensorProto .FLOAT , (2 , 3 , 4 , 5 ))],
168
+ [helper .make_tensor_value_info ("Z1" , TensorProto .INT64 , [4 ])],
169
+ )
170
+
171
+ model_proto = helper .make_model (graph , producer_name = "onnx-tests" )
172
+ self .run_identity_compare (["Z1" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
173
+ model_proto , remaining_identity_num = 0 )
174
+
175
+ def test_identity_unremovable_identity (self ):
176
+ # should not remove!!
177
+ node1 = helper .make_node ("Identity" , ["X" ], ["Y" ], name = "identity" )
178
+
179
+ graph = helper .make_graph (
180
+ [node1 ],
181
+ "identity-test" ,
182
+ [helper .make_tensor_value_info ("X" , TensorProto .FLOAT , (2 , 3 , 4 , 5 ))],
183
+ [helper .make_tensor_value_info ("Y" , TensorProto .FLOAT , (2 , 3 , 4 , 5 ))],
184
+ )
185
+
186
+ model_proto = helper .make_model (graph , producer_name = "onnx-tests" )
187
+ self .run_identity_compare (["Y" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
188
+ model_proto , remaining_identity_num = 1 )
189
+
190
+ def test_identity_output_as_multiple_graph_outputs (self ):
191
+ # handle case like this, both Identity nodes are graph outputs,
192
+ # Add
193
+ # / \
194
+ # Identity Identity
195
+ # We at most can remove one Identity for this case.
196
+ node1 = helper .make_node ("Add" , ["X" , "X" ], ["Y" ], name = "identity" )
197
+ node2 = helper .make_node ("Identity" , ["Y" ], ["Z1" ], name = "identity2" )
198
+ node3 = helper .make_node ("Identity" , ["Y" ], ["Z2" ], name = "identity3" )
199
+ graph = helper .make_graph (
200
+ [node1 , node2 , node3 ],
201
+ "identity-test" ,
202
+ [helper .make_tensor_value_info ("X" , TensorProto .FLOAT , (2 , 3 , 4 , 5 ))],
203
+ [helper .make_tensor_value_info ("Z1" , TensorProto .FLOAT , (2 , 3 , 4 , 5 )),
204
+ helper .make_tensor_value_info ("Z2" , TensorProto .FLOAT , (2 , 3 , 4 , 5 ))],
205
+ )
206
+
207
+ model_proto = helper .make_model (graph , producer_name = "onnx-tests" )
208
+ self .run_identity_compare (["Z1" , "Z2" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
209
+ model_proto , remaining_identity_num = 1 )
210
+
211
+ def test_identity_in_subgraph_non_graph_output (self ):
212
+ node1 = helper .make_node ("Add" , ["X" , "X" ], ["Y" ], name = "add" )
213
+
214
+ iter_num_value = np .array (1 , dtype = np .int64 )
215
+ node2 = helper .make_node (
216
+ 'Constant' ,
217
+ inputs = [],
218
+ outputs = ['iterate_num_value' ],
219
+ value = helper .make_tensor (
220
+ name = 'iterate_num_value' ,
221
+ data_type = TensorProto .INT64 ,
222
+ dims = iter_num_value .shape ,
223
+ vals = iter_num_value .flatten ().astype (np .int64 ),
224
+ ),
225
+ )
226
+
227
+ cond_value = np .array (True , dtype = np .bool )
228
+ node3 = helper .make_node (
229
+ 'Constant' ,
230
+ inputs = [],
231
+ outputs = ['cond_value' ],
232
+ value = helper .make_tensor (
233
+ name = 'cond_value' ,
234
+ data_type = TensorProto .BOOL ,
235
+ dims = iter_num_value .shape ,
236
+ vals = cond_value .flatten ().astype (np .bool ),
237
+ ),
238
+ )
239
+
240
+ # sub graph
241
+ sub_node1 = helper .make_node ("Add" , ["loop_var_1" , "loop_var_1" ], ["SubY" ], name = "sub_add" )
242
+ sub_node2 = helper .make_node ("Identity" , ["SubY" ], ["SubIdentity1" ], name = "sub_identity_1" )
243
+ sub_node3 = helper .make_node ("Identity" , ["SubIdentity1" ], ["loop_var_out_1" ], name = "sub_identity_2" )
244
+ sub_node4 = helper .make_node ("Identity" , ["loop_condition" ], ["loop_cond_output" ], name = "sub_identity_3" )
245
+ sub_graph = helper .make_graph (
246
+ [sub_node1 , sub_node2 , sub_node3 , sub_node4 ],
247
+ "identity_subgraph-test" ,
248
+ [helper .make_tensor_value_info ("loop_iter_num" , TensorProto .INT64 , (1 ,)), # iteration_num
249
+ helper .make_tensor_value_info ("loop_condition" , TensorProto .BOOL , ()), # condition
250
+ helper .make_tensor_value_info ("loop_var_1" , TensorProto .FLOAT , ()), # loop-carried dependency
251
+ ],
252
+ [helper .make_tensor_value_info ("loop_cond_output" , TensorProto .BOOL , ()),
253
+ helper .make_tensor_value_info ("loop_var_out_1" , TensorProto .FLOAT , ())
254
+ ],
255
+ )
256
+ # sub graph ends
257
+
258
+ loop_node = helper .make_node ("Loop" , ["iterate_num_value" , "cond_value" , "Y" ], ["loop_var_1_output" ],
259
+ name = "loop" , body = sub_graph )
260
+
261
+ node4 = helper .make_node ("Identity" , ["loop_var_1_output" ], ["Z" ], name = "identity" )
262
+ node5 = helper .make_node ("Shape" , ["Z" ], ["Z1" ], name = "shape" )
263
+
264
+ graph = helper .make_graph (
265
+ [node1 , node2 , node3 , loop_node , node4 , node5 ],
266
+ "identity-test" ,
267
+ [helper .make_tensor_value_info ("X" , TensorProto .FLOAT , (2 , 3 , 4 , 5 ))],
268
+ [helper .make_tensor_value_info ("Z1" , TensorProto .INT64 , [4 ])],
269
+ )
270
+
271
+ model_proto = helper .make_model (graph , producer_name = "onnx-tests" )
272
+ self .run_identity_compare (["Z1" ], {"X" : np .random .randn (2 , 3 , 4 , 5 ).astype (np .float32 )},
273
+ model_proto , remaining_identity_num = 0 )
140
274
275
+ # Tranpose Optimizer Tests End
141
276
142
277
if __name__ == "__main__" :
143
278
unittest_main ()
0 commit comments