onnx2tf 1.29.1__py3-none-any.whl → 1.29.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
onnx2tf/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from onnx2tf.onnx2tf import convert, main
2
2
 
3
- __version__ = '1.29.1'
3
+ __version__ = '1.29.3'
onnx2tf/onnx2tf.py CHANGED
@@ -1246,8 +1246,8 @@ def convert(
1246
1246
  # Attach it to the exception for later use
1247
1247
  ex.onnx_op_name = error_onnx_op_name
1248
1248
 
1249
- # If no replacement file was provided, try to generate one automatically
1250
- if not param_replacement_file and input_onnx_file_path:
1249
+ # If no replacement file was provided, optionally try to generate one automatically
1250
+ if not param_replacement_file and input_onnx_file_path and auto_generate_json_on_error:
1251
1251
  info('')
1252
1252
  info(Color.REVERSE(f'Attempting automatic JSON generation due to conversion error'), '=' * 30)
1253
1253
  if error_onnx_op_name:
@@ -1313,6 +1313,11 @@ def convert(
1313
1313
  warn(
1314
1314
  f'Conversion failed and automatic JSON generation could not find a solution after {attempt} attempts.'
1315
1315
  )
1316
+ elif not param_replacement_file and input_onnx_file_path and not auto_generate_json_on_error:
1317
+ warn(
1318
+ 'Conversion failed. Automatic JSON generation on error is disabled by default.\n' +
1319
+ 'Re-run with --auto_generate_json_on_error or provide a parameter replacement JSON file.'
1320
+ )
1316
1321
  # Re-raise the original error
1317
1322
  raise ex
1318
1323
 
@@ -168,6 +168,46 @@ def make_node(
168
168
  )
169
169
 
170
170
  if not is_known_shape:
171
+ def build_tf_pads_from_begin_end(pads_begin, pads_end):
172
+ pads_begin = tf.cast(pads_begin, tf.int32)
173
+ pads_end = tf.cast(pads_end, tf.int32)
174
+ spatial_pads = tf.stack([pads_begin, pads_end], axis=1)
175
+ return tf.concat(
176
+ [
177
+ tf.zeros((1, 2), dtype=tf.int32),
178
+ spatial_pads,
179
+ tf.zeros((1, 2), dtype=tf.int32),
180
+ ],
181
+ axis=0,
182
+ )
183
+
184
+ def calc_extra_padding_with_ceil_dynamic(input_tensor, pads, kernel_shape, dilations, strides):
185
+ input_shape = tf.shape(input_tensor)
186
+ input_spatial = input_shape[1:-1]
187
+ pads_begin = tf.constant(pads[:len(pads) // 2], dtype=tf.int32)
188
+ pads_end = tf.constant(pads[len(pads) // 2:], dtype=tf.int32)
189
+ pads_along_axis = pads_begin + pads_end
190
+ k = tf.constant(kernel_shape, dtype=tf.int32)
191
+ d = tf.constant(dilations, dtype=tf.int32)
192
+ s = tf.constant(strides, dtype=tf.int32)
193
+
194
+ numerator = input_spatial + pads_along_axis - d * (k - 1) - 1
195
+ output_spatial = tf.cast(
196
+ tf.math.ceil(
197
+ tf.cast(numerator, tf.float32) / tf.cast(s, tf.float32) + 1.0
198
+ ),
199
+ tf.int32,
200
+ )
201
+ last_stride_starts = (output_spatial - 1) * s
202
+ last_stride_validity = last_stride_starts < (input_spatial + pads_begin)
203
+
204
+ extra_pads = tf.where(
205
+ last_stride_validity,
206
+ last_stride_starts + (k - 1) * d + 1 - (input_spatial + pads_along_axis),
207
+ tf.zeros_like(input_spatial),
208
+ )
209
+ return extra_pads
210
+
171
211
  def compute_output_spatial_shape_from_tensor(input_tensor, pads, kernel_shape, dilations, strides, ceil_mode=False):
172
212
  input_shape = tf.shape(input_tensor) # Get dynamic shape
173
213
  input_spatial = input_shape[1:-1] # Extract spatial dimensions only (NHWC format)
@@ -223,18 +263,32 @@ def make_node(
223
263
 
224
264
  # extra padding to end side (right, bottom) may be needed when ceil_mode is True
225
265
  # this extra padding should not be counted as padding when count_include_pad is True
226
- if ceil_mode:
227
- extra_pads = \
228
- calc_extra_padding_with_ceil(
229
- input_shape=input_tensor_shape[1:-1],
230
- kernel=kernel_shape,
266
+ if not is_known_shape:
267
+ pads_begin = tf.constant(pads[:len(pads) // 2], dtype=tf.int32)
268
+ pads_end = tf.constant(pads[len(pads) // 2:], dtype=tf.int32)
269
+ if ceil_mode:
270
+ extra_pads = calc_extra_padding_with_ceil_dynamic(
271
+ input_tensor=input_tensor,
231
272
  pads=pads,
273
+ kernel_shape=kernel_shape,
232
274
  dilations=dilations,
233
275
  strides=strides,
234
276
  )
235
- pads = pads[:len(pads) // 2] + [p + e for p, e in zip(pads[len(pads) // 2:], extra_pads)]
236
-
237
- tf_pads = pads
277
+ pads_end = pads_end + extra_pads
278
+ tf_pads = build_tf_pads_from_begin_end(pads_begin, pads_end)
279
+ else:
280
+ if ceil_mode:
281
+ extra_pads = \
282
+ calc_extra_padding_with_ceil(
283
+ input_shape=input_tensor_shape[1:-1],
284
+ kernel=kernel_shape,
285
+ pads=pads,
286
+ dilations=dilations,
287
+ strides=strides,
288
+ )
289
+ pads = pads[:len(pads) // 2] + [p + e for p, e in zip(pads[len(pads) // 2:], extra_pads)]
290
+
291
+ tf_pads = pads
238
292
 
239
293
  elif auto_pad == 'SAME_UPPER':
240
294
  tf_pad_mode = 'SAME'
@@ -305,36 +359,42 @@ def make_node(
305
359
  # 1. when extra padding layer is added and count_include_pad is False
306
360
  # 2. when extra padding layer is not added and count_include_pad is True
307
361
  # 3. when last stride has extra padding due to ceil_mode and count_include_pad is True
308
- if is_explicit_padding and tf_pads != [0] * spatial_size * 2:
362
+ if is_explicit_padding:
309
363
  warn(
310
364
  f'Tensorflow incompatible padding detected. ' \
311
365
  f'Extra pad layer is inserted automatically. '
312
366
  )
313
-
314
- if auto_pad == 'SAME_LOWER':
315
- # switch the order of pads
316
- tf_pads = [i for tup in zip(tf_pads[len(tf_pads) // 2:], tf_pads[:len(tf_pads) // 2]) for i in tup]
317
-
318
- if not count_include_pad and need_multiplier:
319
- average_multiplier = []
320
- for k, non_zero_count in zip(kernel_shape, non_zero_counts):
321
- multiplier = [k / n if n != 0 else 1 for n in non_zero_count]
322
- average_multiplier.append(multiplier)
323
-
324
- # convert to tensorflow padding format
325
- tf_pads = \
326
- [[0, 0]] + \
327
- [list(i) for i in zip(tf_pads[:len(tf_pads) // 2], tf_pads[len(tf_pads) // 2:])] + \
328
- [[0, 0]]
329
-
330
- if spatial_size == 1 and kernel_shape[0] > input_tensor_shape[1]:
331
- padded_tensor = input_tensor
332
- else:
367
+ if not is_known_shape:
333
368
  padded_tensor = tf.pad(
334
369
  tensor=input_tensor,
335
370
  paddings=tf_pads,
336
371
  mode='CONSTANT',
337
372
  )
373
+ else:
374
+ if auto_pad == 'SAME_LOWER':
375
+ # switch the order of pads
376
+ tf_pads = [i for tup in zip(tf_pads[len(tf_pads) // 2:], tf_pads[:len(tf_pads) // 2]) for i in tup]
377
+
378
+ if not count_include_pad and need_multiplier:
379
+ average_multiplier = []
380
+ for k, non_zero_count in zip(kernel_shape, non_zero_counts):
381
+ multiplier = [k / n if n != 0 else 1 for n in non_zero_count]
382
+ average_multiplier.append(multiplier)
383
+
384
+ # convert to tensorflow padding format
385
+ tf_pads = \
386
+ [[0, 0]] + \
387
+ [list(i) for i in zip(tf_pads[:len(tf_pads) // 2], tf_pads[len(tf_pads) // 2:])] + \
388
+ [[0, 0]]
389
+
390
+ if spatial_size == 1 and kernel_shape[0] > input_tensor_shape[1]:
391
+ padded_tensor = input_tensor
392
+ else:
393
+ padded_tensor = tf.pad(
394
+ tensor=input_tensor,
395
+ paddings=tf_pads,
396
+ mode='CONSTANT',
397
+ )
338
398
 
339
399
  else:
340
400
  padded_tensor = input_tensor
@@ -345,7 +405,7 @@ def make_node(
345
405
  multiplier = [n / k for n in non_zero_count]
346
406
  average_multiplier.append(multiplier)
347
407
 
348
- if count_include_pad and extra_pads != [0] * spatial_size:
408
+ if count_include_pad and is_known_shape and extra_pads != [0] * spatial_size:
349
409
  # extra padding in last stride should not be included in averaging
350
410
  if average_multiplier is None:
351
411
  average_multiplier = []
@@ -370,7 +430,7 @@ def make_node(
370
430
  # Generation of TF OP
371
431
  tf_op_type = None
372
432
  if len(kernel_shape) == 1:
373
- if kernel_shape[0] > padded_tensor.shape[1]:
433
+ if padded_tensor.shape[1] is not None and kernel_shape[0] > padded_tensor.shape[1]:
374
434
  pooled_tensor = AveragePooling1D(
375
435
  pool_size=[padded_tensor.shape[1]],
376
436
  strides=[padded_tensor.shape[1]],
onnx2tf/ops/Conv.py CHANGED
@@ -370,6 +370,20 @@ def make_node(
370
370
  )
371
371
 
372
372
  def depth_conv_bias(input_tensor, input_weights, pad_mode, strides, dilations, input_bias):
373
+ # tf.nn.depthwise_conv2d uses a different output shape when dilation>1 and stride>1.
374
+ # Emulate stride>1 by running stride=1 then subsampling to match ONNX.
375
+ if pad_mode == 'VALID' \
376
+ and max(dilations) > 1 \
377
+ and any(s > 1 for s in strides[1:-1]):
378
+ conv = tf.nn.depthwise_conv2d(
379
+ input=input_tensor,
380
+ filter=input_weights,
381
+ padding=pad_mode,
382
+ strides=[1, 1, 1, 1],
383
+ dilations=dilations,
384
+ )
385
+ conv = conv[:, ::strides[1], ::strides[2], :]
386
+ return tf.add(conv, input_bias)
373
387
  return \
374
388
  tf.add(
375
389
  tf.nn.depthwise_conv2d(
@@ -438,6 +452,19 @@ def make_node(
438
452
  )
439
453
 
440
454
  def depth_conv_nobias(input_tensor, input_weights, pad_mode, strides, dilations):
455
+ # tf.nn.depthwise_conv2d uses a different output shape when dilation>1 and stride>1.
456
+ # Emulate stride>1 by running stride=1 then subsampling to match ONNX.
457
+ if pad_mode == 'VALID' \
458
+ and max(dilations) > 1 \
459
+ and any(s > 1 for s in strides[1:-1]):
460
+ conv = tf.nn.depthwise_conv2d(
461
+ input=input_tensor,
462
+ filter=input_weights,
463
+ padding=pad_mode,
464
+ strides=[1, 1, 1, 1],
465
+ dilations=dilations,
466
+ )
467
+ return conv[:, ::strides[1], ::strides[2], :]
441
468
  return \
442
469
  tf.nn.depthwise_conv2d(
443
470
  input=input_tensor,