GithubHelp home page GithubHelp logo

pinto0309 / onnx2tf Goto Github PK

View Code? Open in Web Editor NEW
559.0 9.0 59.0 3.69 MB

Self-Created Tools to convert ONNX files (NCHW) to TensorFlow/TFLite/Keras format (NHWC). The purpose of this tool is to solve the massive Transpose extrapolation problem in onnx-tensorflow (onnx-tf). I don't need a Star, but give me a pull request.

License: MIT License

Python 99.89% Dockerfile 0.11%
deep-learning machine-learning model-converter models onnx tensorflow tensorflow-lite tflite docker onnx-tensorflow

onnx2tf's Introduction

onnx2tf

Self-Created Tools to convert ONNX files (NCHW) to TensorFlow/TFLite/Keras format (NHWC). The purpose of this tool is to solve the massive Transpose extrapolation problem in onnx-tensorflow (onnx-tf). I don't need a Star, but give me a pull request. Since I am adding challenging model optimizations and fixing bugs almost daily, I frequently embed potential bugs that would otherwise break through CI's regression testing. Therefore, if you encounter new problems, I recommend that you try a package that is a few versions older, or try the latest package that will be released in a few days.

Downloads GitHub Python PyPI CodeQL Model Convert Test Status DOI

Model Conversion Status

https://github.com/PINTO0309/onnx2tf/wiki/model_status

Supported layers

  • https://github.com/onnx/onnx/blob/main/docs/Operators.md

  • ✔️: Supported ✅: Partial support Help wanted: Pull Request are welcome

    See the list of supported layers
    OP Status
    Abs ✔️
    Acosh ✔️
    Acos ✔️
    Add ✔️
    And ✔️
    ArgMax ✔️
    ArgMin ✔️
    Asinh ✔️
    Asin ✔️
    Atanh ✔️
    Atan ✔️
    AveragePool ✔️
    BatchNormalization ✔️
    Bernoulli ✔️
    BitShift ✔️
    BitwiseAnd Help wanted
    BitwiseNot Help wanted
    BitwiseOr Help wanted
    BitwiseXor Help wanted
    Cast ✔️
    Ceil ✔️
    Celu ✔️
    CenterCropPad Help wanted
    Clip ✔️
    Col2Im
    Compress ✔️
    ConcatFromSequence ✔️
    Concat ✔️
    ConstantOfShape ✔️
    Constant ✔️
    Conv ✔️
    ConvInteger
    ConvTranspose ✔️
    Cosh ✔️
    Cos ✔️
    CumSum ✔️
    DeformConv Help wanted
    DepthToSpace ✔️
    Det ✔️
    DequantizeLinear ✔️
    DFT Help wanted
    Div ✔️
    Dropout ✔️
    DynamicQuantizeLinear ✔️
    Einsum ✔️
    Elu ✔️
    Equal ✔️
    Erf ✔️
    Expand ✔️
    Exp ✔️
    EyeLike ✔️
    Flatten ✔️
    Floor ✔️
    FusedConv ✔️
    GatherElements ✔️
    GatherND ✔️
    Gather ✔️
    Gelu ✔️
    Gemm ✔️
    GlobalAveragePool ✔️
    GlobalLpPool ✔️
    GlobalMaxPool ✔️
    GreaterOrEqual ✔️
    Greater ✔️
    GridSample
    GroupNormalization Help wanted
    GRU ✔️
    HammingWindow
    HannWindow
    Hardmax ✔️
    HardSigmoid ✔️
    HardSwish ✔️
    Identity ✔️
    If ✔️
    Input ✔️
    InstanceNormalization ✔️
    Inverse ✔️
    IsInf ✔️
    IsNaN ✔️
    LayerNormalization ✔️
    LeakyRelu ✔️
    LessOrEqual ✔️
    Less ✔️
    Log ✔️
    LogSoftmax ✔️
    Loop Help wanted
    LpNormalization ✔️
    LRN ✔️
    LSTM ✔️
    MatMul ✔️
    MatMulInteger ✔️
    MaxPool ✔️
    Max ✔️
    MaxRoiPool Help wanted
    MaxUnpool ✔️
    Mean ✔️
    MeanVarianceNormalization ✔️
    MelWeightMatrix ✔️
    Min ✔️
    Mish ✔️
    Mod ✔️
    Mul ✔️
    Multinomial ✔️
    Neg ✔️
    NonMaxSuppression ✔️
    NonZero ✔️
    Optional Help wanted
    OptionalGetElement ✔️
    OptionalHasElement ✔️
    Not ✔️
    OneHot ✔️
    Or ✔️
    Pad ✔️
    Pow ✔️
    PRelu ✔️
    QLinearAdd ✔️
    QLinearConcat ✔️
    QLinearConv ✔️
    QLinearLeakyRelu ✔️
    QLinearMatMul ✔️
    QLinearMul ✔️
    QLinearSigmoid ✔️
    QLinearSoftmax ✔️
    QuantizeLinear ✔️
    RandomNormalLike ✔️
    RandomNormal ✔️
    RandomUniformLike ✔️
    RandomUniform ✔️
    Range ✔️
    Reciprocal ✔️
    ReduceL1 ✔️
    ReduceL2 ✔️
    ReduceLogSum ✔️
    ReduceLogSumExp ✔️
    ReduceMax ✔️
    ReduceMean ✔️
    ReduceMin ✔️
    ReduceProd ✔️
    ReduceSum ✔️
    ReduceSumSquare ✔️
    Relu ✔️
    Reshape ✔️
    Resize ✔️
    ReverseSequence ✔️
    RNN ✔️
    RoiAlign ✔️
    Round ✔️
    ScaleAndTranslate ✔️
    Scatter ✔️
    ScatterElements ✔️
    ScatterND ✔️
    Scan Help wanted
    Selu ✔️
    SequenceAt ✔️
    SequenceConstruct ✔️
    SequenceEmpty ✔️
    SequenceErase ✔️
    SequenceInsert ✔️
    SequenceLength ✔️
    Shape ✔️
    Shrink ✔️
    Sigmoid ✔️
    Sign ✔️
    Sinh ✔️
    Sin ✔️
    Size ✔️
    Slice ✔️
    Softmax ✔️
    Softplus ✔️
    Softsign ✔️
    SpaceToDepth ✔️
    Split ✔️
    SplitToSequence ✔️
    Sqrt ✔️
    Squeeze ✔️
    STFT
    StringNormalizer
    Sub ✔️
    Sum ✔️
    Tanh ✔️
    Tan ✔️
    TfIdfVectorizer Help wanted
    ThresholdedRelu ✔️
    Tile ✔️
    TopK ✔️
    Transpose ✔️
    Trilu ✔️
    Unique ✔️
    Unsqueeze ✔️
    Upsample ✔️
    Where ✔️
    Xor ✔️

Demo

Video speed is adjusted approximately 50 times slower than actual speed. render1665941718294

Environment

  • Linux / Windows
  • onnx==1.15.0
  • onnxruntime==1.17.1
  • onnx-simplifier==0.4.33 or 0.4.30 (onnx.onnx_cpp2py_export.shape_inference.InferenceError: [ShapeInferenceError] (op_type:Slice, node name: /xxxx/Slice): [ShapeInferenceError] Inferred shape and existing shape differ in rank: (x) vs (y))
  • onnx_graphsurgeon
  • simple_onnx_processing_tools
  • tensorflow==2.16.1, Note: #515, Special bugs: #436
  • psutil==5.9.5
  • ml_dtypes==0.3.2
  • flatbuffers-compiler (Optional, Only when using the -coion option. Executable file named flatc.)
    # Custom flatc v23.5.26 binary for Ubuntu 20.04+
    # https://github.com/PINTO0309/onnx2tf/issues/196
    wget https://github.com/PINTO0309/onnx2tf/releases/download/1.16.31/flatc.tar.gz \
    && tar -zxvf flatc.tar.gz \
    && sudo chmod +x flatc \
    && sudo mv flatc /usr/bin/

Sample Usage

1. Install

Note: If you are using TensorFlow v2.13.0 or earlier, use a version older than onnx2tf v1.17.5. onnx2tf v1.17.6 or later will not work properly due to changes in TensorFlow's API. See: #515

  • HostPC
    # PAT authentication is required to pull from GHCR.
    docker login ghcr.io
    
    Username (xxxx): {Enter}
    Password: {Personal Access Token}
    Login Succeeded
    
    docker run --rm -it \
    -v `pwd`:/workdir \
    -w /workdir \
    ghcr.io/pinto0309/onnx2tf:1.20.1
    
    or
    
    # Authentication is not required for pulls from Docker Hub.
    docker run --rm -it \
    -v `pwd`:/workdir \
    -w /workdir \
    docker.io/pinto0309/onnx2tf:1.20.1
    
    or
    
    pip install -U onnx==1.15.0 \
    && pip install -U nvidia-pyindex \
    && pip install -U onnx-graphsurgeon \
    && pip install -U onnxruntime==1.17.1 \
    && pip install -U onnxsim==0.4.33 \
    && pip install -U simple_onnx_processing_tools \
    && pip install -U tensorflow==2.16.1 \
    && pip install -U protobuf==3.20.3 \
    && pip install -U onnx2tf \
    && pip install -U h5py==3.11.0 \
    && pip install -U psutil==5.9.5 \
    && pip install -U ml_dtypes==0.3.2 \
    && pip install -U tf-keras~=2.16
    
    or
    
    pip install -e .

or

  • Google Colaboratory Python3.10
    !sudo apt-get -y update
    !sudo apt-get -y install python3-pip
    !sudo apt-get -y install python-is-python3
    !wget https://github.com/PINTO0309/onnx2tf/releases/download/1.16.31/flatc.tar.gz \
      && tar -zxvf flatc.tar.gz \
      && sudo chmod +x flatc \
      && sudo mv flatc /usr/bin/
    !pip install -U pip \
      && pip install tensorflow==2.16.1 \
      && pip install -U onnx==1.15.0 \
      && python -m pip install onnx_graphsurgeon \
            --index-url https://pypi.ngc.nvidia.com \
      && pip install -U onnxruntime==1.17.1 \
      && pip install -U onnxsim==0.4.33 \
      && pip install -U simple_onnx_processing_tools \
      && pip install -U onnx2tf \
      && pip install -U protobuf==3.20.3 \
      && pip install -U h5py==3.11.0 \
      && pip install -U psutil==5.9.5 \
      && pip install -U ml_dtypes==0.3.2 \
      && pip install -U tf-keras~=2.16
    

2. Run test

Only patterns that are considered to be used particularly frequently are described. In addition, there are several other options, such as disabling Flex OP and additional options to improve inference performance. See: CLI Parameter

# Float32, Float16
# This is the fastest way to generate tflite,
# but the accompanying saved_model will not have a signature.
# "ValueError: Only support at least one signature key."
# If you are having trouble with this error, please use the `-osd` option.
wget https://github.com/PINTO0309/onnx2tf/releases/download/0.0.2/resnet18-v1-7.onnx
onnx2tf -i resnet18-v1-7.onnx

# saved_model with signaturedefs added.
# Output in the form of saved_model that can be used for serving
# or conversion to other frameworks. e.g. TensorFlow.js, CoreML, etc
# https://github.com/PINTO0309/onnx2tf#16-conversion-to-tensorflowjs
# https://github.com/PINTO0309/onnx2tf#17-conversion-to-coreml
wget https://github.com/PINTO0309/onnx2tf/releases/download/0.0.2/resnet18-v1-7.onnx
onnx2tf -i resnet18-v1-7.onnx -osd

# In the interest of efficiency for my development and debugging of onnx2tf,
# the default configuration shows a large amount of debug level logs.
# However, for most users, a large number of debug logs are unnecessary.
# If you want to reduce the amount of information displayed in the conversion log,
# you can change the amount of information in the log by specifying the
# `--verbosity` or `-v` option as follows.
# Possible values are "debug", "info", "warn", and "error".
wget https://github.com/PINTO0309/onnx2tf/releases/download/0.0.2/resnet18-v1-7.onnx
onnx2tf -i resnet18-v1-7.onnx -v info

# Override undefined batch size or other dimensions with static values.
# If the model has undefined dimensions, rewriting them to a static size will significantly
# improve the success rate of the conversion.
# The `-b` option overwrites the zero-dimensional batch size with the number specified
# without input OP name.
# Note that if there are multiple input OPs, the zero dimension of all input OPs is
# forced to be rewritten.
# The `-ois` option allows undefined dimensions in all dimensions, including
# the zero dimensionality, to be overwritten to a static shape, but requires
# the input OP name to be specified.
# e.g. -ois data1:1,3,224,224 data2:1,255 data3:1,224,6
wget https://github.com/PINTO0309/onnx2tf/releases/download/0.0.2/resnet18-v1-7.onnx
onnx2tf -i resnet18-v1-7.onnx -b 1
or
onnx2tf -i resnet18-v1-7.onnx -ois data:1,3,224,224

# Suppress automatic transposition of input OPs from NCW, NCHW, NCDHW to NWC, NHWC, NDHWC.
# onnx2tf is a specification that automatically transposes the input OP to [N,H,W,C] format
# before converting the model. However, since onnx2tf cannot determine from the structure of
# the model whether the input data is image, audio data, or something else, it unconditionally
# transposes the channels. Therefore, it is the models of STT/TTS models where the input is
# not NHWC that tend to have particular problems with the automatic transposition of the
# input OP.
# If you do not want input OPs to be automatically transposed, you can disable automatic
# transposition of input OPs by specifying the `-kat` option.
wget https://github.com/PINTO0309/onnx2tf/releases/download/1.1.28/double_gru.onnx
# INPUT OPs: "spec": float32[1,3,257,1], "states_in": float32[2,1,32]
# The following command suppresses the automatic transposition of "states_in" and converts it.
onnx2tf -i double_gru.onnx -kat states_in

# Keras h5 format
# .h5, .json, .keras, .weights.h5, .weights.keras, .data-00000-of-00001, .index
wget https://github.com/PINTO0309/onnx2tf/releases/download/0.0.2/resnet18-v1-7.onnx
onnx2tf -i resnet18-v1-7.onnx -oh5

# Keras keras_v3 format (TensorFlow v2.12.0 or later only)
wget https://github.com/PINTO0309/onnx2tf/releases/download/0.0.2/resnet18-v1-7.onnx
onnx2tf -i resnet18-v1-7.onnx -okv3

# TensorFlow v1 (.pb) format
wget https://github.com/PINTO0309/onnx2tf/releases/download/0.0.2/resnet18-v1-7.onnx
onnx2tf -i resnet18-v1-7.onnx -otfv1pb

# INT8 Quantization, Full INT8 Quantization
# INT8 Quantization with INT16 activation, Full INT8 Quantization with INT16 activation
# Dynamic Range Quantization
wget https://github.com/PINTO0309/onnx2tf/releases/download/1.1.1/emotion-ferplus-8.onnx
# INT8 Quantization (per-channel)
onnx2tf -i emotion-ferplus-8.onnx -oiqt
# INT8 Quantization (per-tensor)
onnx2tf -i emotion-ferplus-8.onnx -oiqt -qt per-tensor

# Split the model at the middle position for debugging
# Specify the output name of the OP
onnx2tf -i resnet18-v1-7.onnx -onimc resnetv15_stage2_conv1_fwd resnetv15_stage2_conv2_fwd

# Suppress generation of Flex OP and replace with Pseudo-Function
# [Asin, Acos, Atan, Abs, PReLU, LeakyReLU, Power, GatherND, Neg, HardSwish, Erf, GeLU]
# Below is a sample of replacing Erf with another set of operations.
wget https://s3.ap-northeast-2.wasabisys.com/temp-models/onnx2tf_readme/Erf_11.onnx
onnx2tf -i Erf_11.onnx -rtpo Erf

# High-dimensional Transpose decomposition
# If you do not like FlexTranspose being generated, try `-nodaftc`.
# Suppresses the generation of FlexTranspose by decomposing Transpose
# to the specified number of dimensions.
# In TensorFlow v2.12.0 and later, up to 6 dimensions are converted to normal Transpose;
# in v2.11.0 and earlier, up to 5 dimensions are converted to normal Transpose.
# Note that specifying `2` for the `-nodaftc` option causes all Transpose OPs to disappear
# from the model structure.
# Below is an example of decomposing a Transpose of 5 or more dimensions into a Transpose
# of 4 dimensions.
onnx2tf -i xxxx.onnx -nodaftc 4

# High-dimensional Slice(StridedSlice) decomposition
# If your special circumstances do not allow you to deploy a `StridedSlice` with more than
# 5 dimensions to a device, you can use the `-nodafsc` option to decompose the `StridedSlice`
# into a process with 4 or fewer dimensions.
# Below is an example of decomposing a `StridedSlice` of 5 or more dimensions into a
# `StridedSlice` of 4 dimensions.
onnx2tf -i xxxx.onnx -nodafsc 4

# Float16 inference doubling on devices with ARM64 ARMv8.2 or higher instruction set
# Double the inference speed with Float16 precision tflite models on devices with
# high-performance CPUs such as Snapdragon.
# (Pixel 3a, Pixel 5a, Pixel 7, Galaxy M12 and Galaxy S22, ...)
# XNNPACK float16 inference on certain ARM64 cores is 2x faster.
# Unfortunately, Float16 inference cannot be accelerated when using the RaspberryPi4's
# ARM64 CPU.
onnx2tf -i xxxx.onnx -eatfp16

# Parameter replacement (Resize,Transpose,Softmax)
rm replace.json
wget https://github.com/PINTO0309/onnx2tf/releases/download/1.1.27/human_segmentation_pphumanseg_2021oct.onnx
wget https://github.com/PINTO0309/onnx2tf/releases/download/1.1.27/replace.json
onnx2tf -i human_segmentation_pphumanseg_2021oct.onnx -prf replace.json

3. Accuracy check

Perform error checking of ONNX output and TensorFlow output. Verify that the error of all outputs, one operation at a time, is below a certain threshold. Automatically determines before and after which OPs the tool's automatic conversion of the model failed. Know where dimensional compression, dimensional expansion, and dimensional transposition by Reshape and Traspose are failing. Once you have identified the problem area, you can refer to the tutorial on Parameter replacement to modify the tool's behavior.

After many upgrades, the need for JSON parameter correction has become much less common, but there are still some edge cases where JSON correction is required. If the PC has sufficient free space in its RAM, onnx2tf will convert the model while carefully performing accuracy checks on all OPs. Thus, at the cost of successful model conversion, the conversion speed is a little slower. If the amount of RAM required for the accuracy check is expected to exceed 80% of the total available RAM capacity of the entire PC, the conversion operation will be performed without an accuracy check. Therefore, if the accuracy of the converted model is found to be significantly degraded, the accuracy may be automatically corrected by re-conversion on a PC with a large amount of RAM. For example, my PC has 128GB of RAM, but the StableDiffusion v1.5 model is too complex in its structure and consumed about 180GB of RAM in total with 50GB of SWAP space.

-ois an option to overwrite the input OP to a static size if it has undefined dimensions. -cotof option checks the accuracy of all OPs one by one. -cotoa is the error value of the threshold for determining an accuracy error. If there are undefined dimensions in the input OP, it is better to fix them to the static geometry to improve the accuracy of the accuracy measurement.

Also, you can use the -cind option to specify custom input for -cotof, instead of using the default dummy input. Otherwise, all input values will be set to 1. For more information about the -cind option, please refer to here.

The -cotof option only compares the original ONNX and converted TensorFlow (Keras) models at Float32 precision, not at Float16 or INT8 precision.

onnx2tf -i mobilenetv2-12.onnx -ois input:1,3,224,224 -cotof -cotoa 1e-1

or

onnx2tf -i mobilenetv2-12.onnx -b 1 -cotof -cotoa 1e-1

or

onnx2tf -i mobilenetv2-12.onnx -cotof -cotoa 1e-1 -cind "input" "/your/path/x.npy"

image

Kazam_screencast_00108_

4. Match tflite input/output names and input/output order to ONNX

If you want to match tflite's input/output OP names and the order of input/output OPs with ONNX, you can use the interpreter.get_signature_runner() to infer this after using the -coion / --copy_onnx_input_output_names_to_tflite option to output tflite file. See: #228

import torch
import onnxruntime
import numpy as np
import onnx2tf
import tensorflow as tf
from tensorflow.lite.python import interpreter as tflite_interpreter

class Model(torch.nn.Module):
    def forward(self, x, y):
        return {
            "add": x + y,
            "sub": x - y,
        }

# Let's double check what PyTorch gives us
model = Model()
pytorch_output = model.forward(10, 2)
print("[PyTorch] Model Predictions:", pytorch_output)

# First, export the above model to ONNX
torch.onnx.export(
    Model(),
    {"x": 10, "y": 2},
    "model.onnx",
    opset_version=16,
    input_names=["x", "y"],
    output_names=["add", "sub"],
)

# And check its output
session = onnxruntime.InferenceSession("model.onnx")
onnx_output = session.run(["add", "sub"], {"x": np.array(10), "y": np.array(2)})
print("[ONNX] Model Outputs:", [o.name for o in session.get_outputs()])
print("[ONNX] Model Predictions:", onnx_output)

# Now, let's convert the ONNX model to TF
onnx2tf.convert(
    input_onnx_file_path="model.onnx",
    output_folder_path="model.tf",
    copy_onnx_input_output_names_to_tflite=True,
    non_verbose=True,
)

# Now, test the newer TFLite model
interpreter = tf.lite.Interpreter(model_path="model.tf/model_float32.tflite")
tf_lite_model = interpreter.get_signature_runner()
inputs = {
  'x': np.asarray([10], dtype=np.int64),
  'y': np.asarray([2], dtype=np.int64),
}
tf_lite_output = tf_lite_model(**inputs)
print("[TFLite] Model Predictions:", tf_lite_output)
[PyTorch] Model Predictions:
  {
    'add': 12,
    'sub': 8
  }
[ONNX] Model Outputs:
  [
    'add',
    'sub'
  ]
[ONNX] Model Predictions:
  [
    array(12, dtype=int64),
    array(8, dtype=int64)
  ]
[TFLite] Model Predictions:
  {
    'add': array([12]),
    'sub': array([8])
  }

image

5. Rewriting of tflite input/output OP names and signature_defs

If you do not like tflite input/output names such as serving_default_*:0 or StatefulPartitionedCall:0, you can rewrite them using the following tools and procedures. It can be rewritten from any name to any name, so it does not have to be serving_default_*:0 or StatefulPartitionedCall:0.

https://github.com/PINTO0309/tflite-input-output-rewriter

# Install custom flatc
wget https://github.com/PINTO0309/onnx2tf/releases/download/1.7.3/flatc.tar.gz \
&& tar -zxvf flatc.tar.gz \
&& sudo chmod +x flatc \
&& sudo mv flatc /usr/bin/ \
&& rm flatc.tar.gz

# Path check
which flatc
/usr/bin/flatc

# Install tfliteiorewriter
pip install -U tfliteiorewriter
  • Before

    01

    tfliteiorewriter \
    -i xxxx.tflite \
    -r serving_default_input_1:0 aaa \
    -r StatefulPartitionedCall:0 bbb

    02

  • After

    03

6. Embed metadata in tflite

If you want to embed label maps, quantization parameters, descriptions, etc. into your tflite file, you can refer to the official tutorial and try it yourself. For now, this tool does not plan to implement the ability to append metadata, as I do not want to write byte arrays to the tflite file that are not essential to its operation.

7. If the accuracy of the INT8 quantized model degrades significantly

It is a matter of model structure. The activation function (SiLU/Swish), kernel size and stride for Pooling, and kernel size and stride for Conv should be completely revised. See: #244 (comment), and #269

If you want to see the difference in quantization error between SiLU and ReLU, please check this Gist by @motokimura who helped us in our research. Thanks Motoki!

Gist: Quantization error simulation of SiLU (Swish) activation

The accuracy error rates after quantization for different activation functions are shown in the figure below. The graph plots the distribution of absolute error, so a position with a higher value on the horizontal axis indicates a larger error. The vertical axis is the number of samples. SiLU (Swish) produces catastrophic errors after INT8 quantization.

image

  • e.g. YOLOX-Nano

    • https://github.com/motokimura/yolox-ti-lite_tflite

    • https://github.com/TexasInstruments/edgeai-yolox

      Before After
      Swish/SiLU
      image
      ReLU
      image
      DepthwiseConv2D
      image
      Conv2D
      image
      MaxPool, kernel_size=5x5,9x9,13x13
      image
      MaxPool, kernel_size=3x3
      image
      ### Float32 - YOLOX-Nano
      (1, 52, 52, 85)
      array([[[
          [ 0.971787,  0.811184,  0.550566, ..., -5.962632, -7.403673, -6.735206],
          [ 0.858804,  1.351296,  1.231673, ..., -6.479690, -8.277064, -7.664936],
          [ 0.214827,  1.035119,  1.458006, ..., -6.291425, -8.229385, -7.761562],
              ...,
          [ 0.450116,  1.391900,  1.533354, ..., -5.672194, -7.121591, -6.880231],
          [ 0.593133,  2.112723,  0.968755, ..., -6.150078, -7.370633, -6.874294],
          [ 0.088263,  1.985220,  0.619998, ..., -5.507928, -6.914980, -6.234259]]]]),
      
      ### INT8 - YOLOX-Nano
      (1, 52, 52, 85)
      array([[[
          [ 0.941908,  0.770652,  0.513768, ..., -5.993958, -7.449634, -6.850238],
          [ 0.856280,  1.284420,  1.198792, ..., -6.507727, -8.391542, -7.792146],
          [ 0.256884,  0.941908,  1.455676, ..., -6.336471, -8.305914, -7.877774],
              ...,
          [ 0.342512,  1.370048,  1.541304, ..., -5.737075, -7.192750, -7.107122],
          [ 0.513768,  2.226327,  1.027536, ..., -6.165215, -7.449634, -7.021494],
          [ 0.085628,  2.055072,  0.685024, ..., -5.480191, -7.021494, -6.422099]]]]),
      
  • Other recommended replacement OP

    Before After
    HardSwish
    image
    ReLU
    image
    ReLU6
    image
    Paper: A Quantization-Friendly Separable Convolution for MobileNets https://arxiv.org/pdf/1803.08607.pdf
    ReLU
    image
  • Quantization range collapse due to non-zero constant padding

    If padding is performed with a constant other than zero, the padding value may destroy the quantization range of the input tensor. For example, the pattern is shown in the figure below. The MaxPool2D is done after padding the 4 sides of the input tensor with the minimum value of Float32. It seems that if INT8 quantization is performed with this structure, the quantization range is determined by MaxPool2D during quantization, including the values padded to the tensor. See: #444 image

    Therefore, the following two similar examples are equally likely to result in divergent output values for the model after INT8 quantization, with all output values being Nan or zero.

    1. Pattern with fixed value -255.0 padded on 4 sides of tensor image

    2. Pattern with fixed value -128.0 padded on 4 sides of tensor image

8. Calibration data creation for INT8 quantization

Calibration data (.npy) for INT8 quantization (-cind) is generated as follows. This is a sample when the data used for training is image data. See: #222

https://www.tensorflow.org/lite/performance/post_training_quantization

import cv2
import glob
import numpy as np

# Not used during data generation ################################
# You will need to do the calculations yourself using the test data
MEAN = np.asarray([[[[0.485, 0.456, 0.406]]]], dtype=np.float32) # [1,1,1,3]
STD = np.asarray([[[[0.229, 0.224, 0.225]]]], dtype=np.float32) # [1,1,1,3]
# Not used during data generation ################################

files = glob.glob("data/*.png")
img_datas = []
for idx, file in enumerate(files):
    bgr_img = cv2.imread(file)
    rgb_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2RGB)
    resized_img = cv2.resize(rgb_img, dsize=(200,112))
    extend_batch_size_img = resized_img[np.newaxis, :]
    normalized_img = extend_batch_size_img / 255.0 # 0.0 - 1.0
    print(
        f'{str(idx+1).zfill(2)}. extend_batch_size_img.shape: {extend_batch_size_img.shape}'
    ) # [1,112,200,3]
    img_datas.append(extend_batch_size_img)
calib_datas = np.vstack(img_datas)
print(f'calib_datas.shape: {calib_datas.shape}') # [10,112,200,3]
np.save(file='data/calibdata.npy', arr=calib_datas)

loaded_data = np.load('data/calibdata.npy')
print(f'loaded_data.shape: {loaded_data.shape}') # [10,112,200,3]

"""
-cind INPUT_NAME NUMPY_FILE_PATH MEAN STD
int8_calib_datas = (loaded_data - MEAN) / STD # -1.0 - 1.0

e.g. How to specify calibration data in CLI or Script respectively.
1. CLI
  -cind "pc_dep" "data/calibdata.npy" "[[[[0.485,0.456,0.406]]]]" "[[[[0.229,0.224,0.225]]]]"
  -cind "feat" "data/calibdata2.npy" "[[[[0.123,...,0.321]]]]" "[[[[0.112,...,0.451]]]]"

2. Script
  custom_input_op_name_np_data_path=[
      ["pc_dep", "data/calibdata.npy", [[[[0.485,0.456,0.406]]]], [[[[0.229,0.224,0.225]]]]],
      ["feat", "data/calibdata2.npy", [[[[0.123,...,0.321]]]], [[[[0.112,...,0.451]]]],
  ]
"""

9. INT8 quantization of models with multiple inputs requiring non-image data

If you do not need to perform INT8 quantization with this tool alone, the following method is the easiest.

The -osd option will output a saved_model.pb in the saved_model folder with the full size required for quantization. That is, a default signature named serving_default is embedded in .pb. The -b option is used to convert the batch size by rewriting it as a static integer.

Note: INT8 TFLite generated by following this procedure as is will result in a model with significantly degraded accuracy. This tutorial only demonstrates the INT8 quantization procedure; if you wish to correct for accuracy, please refer to Parameter replacement to correct for transposition errors in the operation.

# Ref: https://github.com/onnx/models/tree/main/text/machine_comprehension/bert-squad
wget https://s3.ap-northeast-2.wasabisys.com/temp-models/onnx2tf_248/bertsquad-12.onnx

onnx2tf -i bertsquad-12.onnx -b 1 -osd -cotof

image

Use the saved_model_cli command to check the saved_model signature. INT8 quantization calibration using signatures allows correct control of the input order of data for calibration. Therefore, calibration with signatures is recommended for INT8 quantization of models with multiple inputs.

saved_model_cli show --dir saved_model/ --tag_set serve --signature_def serving_default

The given SavedModel SignatureDef contains the following input(s):
  inputs['input_ids_0'] tensor_info:
      dtype: DT_INT64
      shape: (1, 256)
      name: serving_default_input_ids_0:0
  inputs['input_mask_0'] tensor_info:
      dtype: DT_INT64
      shape: (1, 256)
      name: serving_default_input_mask_0:0
  inputs['segment_ids_0'] tensor_info:
      dtype: DT_INT64
      shape: (1, 256)
      name: serving_default_segment_ids_0:0
  inputs['unique_ids_raw_output___9_0'] tensor_info:
      dtype: DT_INT64
      shape: (1)
      name: serving_default_unique_ids_raw_output___9_0:0

Calibrate by specifying the input OP name displayed in inputs. The np.ones([xxx], dtype=np.int64) part must be replaced with the correct calibration test data. In practice, several pieces of data used for training are extracted and used.

import tensorflow as tf
import numpy as np

def representative_dataset():
    unique_ids = np.ones([10, 256], dtype=np.int64)
    segment_ids = np.ones([10, 256], dtype=np.int64)
    input_masks = np.ones([10, 256], dtype=np.int64)
    input_ids = np.ones([10], dtype=np.int64)

    for unique_id, segment_id, input_mask, input_id \
        in zip(unique_ids, segment_ids, input_masks, input_ids):

        yield {
            "unique_ids_raw_output___9_0": unique_id,
            "segment_ids_0": segment_id,
            "input_mask_0": input_mask,
            "input_ids_0": input_id,
        }

converter = tf.lite.TFLiteConverter.from_saved_model('saved_model')
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8  # or tf.uint8
converter.inference_output_type = tf.int8  # or tf.uint8
tflite_quant_model = converter.convert()

with open('saved_model/int8_model.tflite', 'wb') as w:
    w.write(tflite_quant_model)

image

https://www.tensorflow.org/lite/performance/post_training_quantization

See: #248

10. Fixing the output of NonMaxSuppression (NMS)

PyTorch's NonMaxSuppression (torchvision.ops.nms) and ONNX's NonMaxSuppression are not fully compatible. TorchVision's NMS is very inefficient. Therefore, it is inevitable that converting ONNX using NMS in object detection models and other models will be very redundant and will be converted with a structure that is difficult for TensorFlow.js and TFLite models to take advantage of in devices. This is due to the indefinite number of tensors output by the NMS. In this chapter, I share how to easily tune the ONNX generated using TorchVision's redundant NMS to generate an optimized NMS.

  1. There are multiple issues with TorchVision's NMS. First, the batch size specification is not supported; second, the max_output_boxes_per_class parameter cannot be specified. Please see the NMS sample ONNX part I generated. The max_output_boxes_per_class has been changed to 896 instead of -Infinity. The biggest problem with TorchVision NMS is that it generates ONNX with max_output_boxes_per_class set to -Infinity or 9223372036854775807 (Maximum value of INT64), resulting in a variable number of NMS outputs from zero to infinite. Thus, by rewriting -Infinity or 9223372036854775807 (Maximum value of INT64) to a constant value, it is possible to output an NMS that can be effortlessly inferred by TFJS or TFLite. image

    Here you will find committed ONNX components optimized for various devices. https://github.com/PINTO0309/components_of_onnx/tree/main/components_of_onnx/ops

  2. In the following example, the max_output_boxes_per_class of NMS in the post-processing generated by YOLOv7 is changed from -Infinity or 9223372036854775807 (Maximum value of INT64) to 20, as shown in the figure below. The name main01_max_output_boxes_per_class has been rewritten by me for clarity, but it originally appears as max_output_boxes_per_class. image

    Simply execute the following command. The command rewrites the specified attribute value of the OP specified by ONNX.

    pip install sam4onnx
    
    sam4onnx \
    --op_name main01_nonmaxsuppression11 \
    --input_onnx_file_path yolov7.onnx \
    --output_onnx_file_path nms_yolov7_update.onnx \
    --input_constants main01_max_output_boxes_per_class int64 [20]

    A tutorial on one of my ONNX modification tools, sam4onnx, can be found here.

    https://github.com/PINTO0309/sam4onnx

    Many detailed tutorials are provided below, so if you are interested, please play with them.

    https://github.com/PINTO0309/PINTO_model_zoo/tree/main/307_YOLOv7/post_process_gen_tools

  3. Finally, simply convert ONNX to TFLite or saved_model or TFJS using onnx2tf. onnx2tf performs an internal operation to automatically optimize the NMS output to a fixed shape if max_output_boxes_per_class is set to a value other than -Infinity and 9223372036854775807 (Maximum value of INT64). Specify --output_nms_with_dynamic_tensor or -onwdt if you do not want to optimize for a fixed shape.

    onnx2tf -i nms_yolov7_update.onnx -osd -cotof
    

    I would be happy if this is a reference for Android + Java or TFJS implementations. There are tons more tricky model optimization techniques described in my blog posts, so you'll have to find them yourself. I don't dare to list the URL here because it is annoying to see so many issues being posted. And unfortunately, all articles are in Japanese. image

11. RNN (RNN, GRU, LSTM) Inference Acceleration

TensorFlow's RNN has a speedup option called unroll. The network will be unrolled, else a symbolic loop will be used. Unrolling can speed-up a RNN, although it tends to be more memory-intensive. Unrolling is only suitable for short sequences. onnx2tf allows you to deploy RNNs into memory-intensive operations by specifying the --enable_rnn_unroll or -eru options. The --enable_rnn_unroll option is available for RNN, GRU, and LSTM.

An example of BidirectionalLSTM conversion with the --enable_rnn_unroll option is shown below. Please ignore that the shapes of the input and output tensors do not match, since the samples are shown by picking up separate models.

  • ONNX LSTM (Bidirectional)

    image

  • BidirectionalLSTM with --enable_rnn_unroll option unspecified

    Recurrent layer is implemented from scratch.

    image

  • BidirectionalLSTM with --enable_rnn_unroll option

    image

12. If the accuracy of the Float32 model degrades significantly

The pattern of accuracy degradation of the converted model does not only occur when INT8 quantization is performed. A special edge case is when there is a problem with the implementation of a particular OP on the TFLite runtime side. Below, I will reproduce the problem by means of a very simple CNN model and further explain its workaround. Here is the issue that prompted me to add this explanation. [Conv-TasNet] Facing issue in converting Conv-TasNet model #447

Download a sample model for validation.

curl \
-L https://github.com/PINTO0309/onnx2tf/files/12367312/prelu_check.onnx.zip \
-o prelu_check.onnx.zip

unzip prelu_check.onnx.zip

The part of the downloaded model where the problem occurs is the PRelu part in the figure below.

  • ONNX

    image

Reproduce the problem. The following command converts an ONNX file to a TFLite file.

onnx2tf -i prelu_check.onnx -cotof

The conversion was successful and, as shown in the figure below, the inference test results from ONNX and the inference results for the Float32 model in TensorFlow (Keras) match perfectly. It is important to note that the comparison of inference results between ONNX and TensorFlow transformed models is comparing ONNX models with TensorFlow (Keras) models, not ONNX models with TFLite models.

  • Conversion results

    20230817175146

  • tflite

    20230817175530

Now, let's try inference with the TFLite runtime instead of the TensorFlow runtime.

  • test.py
    import time
    import numpy as np
    np.random.seed(0)
    import tensorflow as tf
    
    # Load TFLite model
    interpreter = tf.lite.Interpreter(model_path="./saved_model/prelu_check_float32.tflite")
    interpreter.allocate_tensors()
    tensor_shape = (256, 20)
    input_data = {'waveform': np.random.randn(*tensor_shape).astype(np.float32)}
    
    # Load and preprocess
    input_details = interpreter.get_input_details()
    input_shape = input_details[0]['shape']
    print(input_shape)
    
    # Run inference
    interpreter.set_tensor(input_details[0]['index'], input_data["waveform"])
    separate_time = time.time()
    interpreter.invoke()
    print("Done! {:.3f} s".format(time.time() - separate_time))
    output_details = interpreter.get_output_details()
    output_data = interpreter.get_tensor(output_details[0]['index'])
    
    output_data = []
    for output_detail in output_details:
        output_data.append(interpreter.get_tensor(output_detail['index']))
    
    print(output_data)

Oddly enough, the output value of PReLU contains multiple nan. However, as can be seen by converting the ONNX model to the middle of the model using the -onimc option, nan does not occur until just before PReLU. Thus, it is clear that the PReLU OP in the TFLite runtime has a problem with divergent inference results.

  • TFLite inference results

    20230817175454

The following is a work-around to avoid this problem. Use the -rtpo option to replace PReLU with a similar primitive operation when transforming a model, and then perform the model transformation.

onnx2tf -i prelu_check.onnx -cotof -rtpo PReLU

As before, the inference results from ONNX and TensorFlow (Keras) match perfectly.

  • Conversion results

    20230817175614

However, -rtpo PReLU will generate a .tflite file with the PRelu OP replaced by a primitive OP combination.

  • tflite

    20230817175724

Again, run the test code to check the inference results. The figure below shows that no nan occurs when inference is performed by replacing the PReLU OP with only combinations of primitive operations. In other words, it is important to know that large arithmetic errors are not only due to the broken structure of the model, but can also be caused by internal implementations such as the TFLite runtime. I have implemented the -rtpo option to replace operators as a work-around to avoid such runtime problems.

  • TFLite inference results

    20230817175701

13. Problem of extremely large calculation error in InstanceNormalization

Even if the conversion is successful, InstanceNormalization tends to have very large errors. This is an ONNX specification.

I verified this with a very simple sample model. There are more than 8 million elements, and the calculation error reached 1e-2.

image

image

14. Inference with dynamic tensors in TFLite

For some time now, TFLite runtime has supported inference by dynamic tensors. However, the existence of this important function is not widely recognized. In this chapter, I will show how I can convert an ONNX file that contains dynamic geometry in batch size directly into a TFLite file that contains dynamic geometry and then further infer it in variable batch conditions. The issue that inspired me to add this tutorial is here. [Dynamic batch / Dynamic shape] onnx model with dynamic input is converted to tflite with static input 1 #441, or Cannot use converted model with dynamic input shape #521

First, download the sample ONNX file.

wget https://s3.ap-northeast-2.wasabisys.com/temp-models/onnx2tf_441/osnet_x0_25_msmt17.onnx

This model calculates the similarity of features by cosine similarity. The batch size dimension of the input tensor is batch, allowing various numbers of images to be input simultaneously. This is often used, for example, to achieve tracking by calculating the similarity of people or objects reflected between successive video frames. However, the total number of objects to be tracked changes rapidly with each video frame because the number of people and objects in the image constantly increases and decreases. Therefore, there is a very significant use case for generating models with variable settings for the number of input images (batch size) of the model.

image

Convert the downloaded OSNet to tflite and saved_model as a variable batch. If you do not specify the -b or -ois options, onnx2tf does not change the batch size as N. The only important point is to convert the model with the -osd and -coion options. Note that if you use the -coion option, you must install flatbuffers-compiler with apt-get install, run the commands for building the environment described first in this README, or use a Docker container.

onnx2tf -i osnet_x0_25_msmt17.onnx -osd -coion
  • .tflite

    When viewing tflite in Netron, the batch size appears to be fixed at 1. image

  • saved_model

    However, checking the structure of saved_model, the batch size is correctly set to -1.

    saved_model_cli show --dir saved_model/ --all
    
    MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:
    
    signature_def['__saved_model_init_op']:
      The given SavedModel SignatureDef contains the following input(s):
      The given SavedModel SignatureDef contains the following output(s):
        outputs['__saved_model_init_op'] tensor_info:
            dtype: DT_INVALID
            shape: unknown_rank
            name: NoOp
      Method name is:
    
    signature_def['serving_default']:
      The given SavedModel SignatureDef contains the following input(s):
        inputs['images'] tensor_info:
            dtype: DT_FLOAT
            shape: (-1, 256, 128, 3)
            name: serving_default_images:0
      The given SavedModel SignatureDef contains the following output(s):
        outputs['output'] tensor_info:
            dtype: DT_FLOAT
            shape: (-1, 512)
            name: PartitionedCall:0
      Method name is: tensorflow/serving/predict

To prove that the tflite structure has been converted correctly, I will convert the tflite to JSON and look at the structure.

docker run --rm -it \
-v `pwd`:/home/user/workdir \
ghcr.io/pinto0309/tflite2json2tflite:latest

./flatc -t \
--strict-json \
--defaults-json \
-o workdir \
./schema.fbs -- workdir/saved_model/osnet_x0_25_msmt17_float32.tflite

ls -l workdir

-rw-rw-r-- 1 user user   921564 Aug  4 10:24 osnet_x0_25_msmt17.onnx
-rw-r--r-- 1 user user 10369524 Aug  4 10:30 osnet_x0_25_msmt17_float32.json
drwxrwxr-x 4 user user     4096 Aug  4 10:26 saved_model

image

  • osnet_x0_25_msmt17_float32.json

    "shape_signature" is correctly set to -1. However, "shape" is set to 1. This could be a problem with TFLiteConverter, or it could be a problem with Netron's graphical display capabilities. image

In other words, although onnx2tf converts TFLiteConverer as specified, with the batch size of -1 without any model processing, only Netron's display is broken. This is a problem I have known for quite some time. However, the inference itself does not cause the problem.

If you want to infer in variable batches, you need to infer using signature. In such cases, the -coion option must be specified when converting the model. Note that I have identified a problem with quantization with the -coion option, which can corrupt tflite files. #429

https://github.com/PINTO0309/onnx2tf#4-match-tflite-inputoutput-names-and-inputoutput-order-to-onnx

  • test.py - Batch size: 5
    import numpy as np
    import tensorflow as tf
    from pprint import pprint
    
    interpreter = tf.lite.Interpreter(model_path="saved_model/osnet_x0_25_msmt17_float32.tflite")
    tf_lite_model = interpreter.get_signature_runner()
    inputs = {
        'images': np.ones([5,256,128,3], dtype=np.float32),
    }
    tf_lite_output = tf_lite_model(**inputs)
    print(f"[TFLite] Model Predictions shape: {tf_lite_output['output'].shape}")
    print(f"[TFLite] Model Predictions:")
    pprint(tf_lite_output)
  • Results
    [TFLite] Model Predictions shape: (5, 512)
    [TFLite] Model Predictions:
    {'output': array([[0.0000000e+00, 2.4730086e-04, 0.0000000e+00, ..., 1.0528549e+00,
            3.7874988e-01, 0.0000000e+00],
           [0.0000000e+00, 2.4730086e-04, 0.0000000e+00, ..., 1.0528549e+00,
            3.7874988e-01, 0.0000000e+00],
           [0.0000000e+00, 2.4730086e-04, 0.0000000e+00, ..., 1.0528549e+00,
            3.7874988e-01, 0.0000000e+00],
           [0.0000000e+00, 2.4730086e-04, 0.0000000e+00, ..., 1.0528549e+00,
            3.7874988e-01, 0.0000000e+00],
           [0.0000000e+00, 2.4730084e-04, 0.0000000e+00, ..., 1.0528525e+00,
            3.7874976e-01, 0.0000000e+00]], dtype=float32)}
    
  • test.py - Batch size: 3
    import numpy as np
    import tensorflow as tf
    from pprint import pprint
    
    interpreter = tf.lite.Interpreter(model_path="saved_model/osnet_x0_25_msmt17_float32.tflite")
    tf_lite_model = interpreter.get_signature_runner()
    inputs = {
        'images': np.ones([3,256,128,3], dtype=np.float32),
    }
    tf_lite_output = tf_lite_model(**inputs)
    print(f"[TFLite] Model Predictions shape: {tf_lite_output['output'].shape}")
    print(f"[TFLite] Model Predictions:")
    pprint(tf_lite_output)
  • Results
    [TFLite] Model Predictions shape: (3, 512)
    [TFLite] Model Predictions:
    {'output': array([[0.0000000e+00, 2.4730084e-04, 0.0000000e+00, ..., 1.0528525e+00,
            3.7874976e-01, 0.0000000e+00],
           [0.0000000e+00, 2.4730084e-04, 0.0000000e+00, ..., 1.0528525e+00,
            3.7874976e-01, 0.0000000e+00],
           [0.0000000e+00, 2.4730084e-04, 0.0000000e+00, ..., 1.0528525e+00,
            3.7874976e-01, 0.0000000e+00]], dtype=float32)}
    

15. Significant optimization of the entire model through Einsum and OneHot optimizations

Einsum and OneHot are not optimized to the maximum by the standard behavior of onnx-optimizer. Therefore, pre-optimizing the Einsum OP and OneHot OP using my original method can significantly improve the success rate of model conversion, and the input ONNX model itself can be significantly optimized compared to when onnxsim alone is optimized. See: #569

For example

python export.py \
--img_size 512 512 \
--lightglue_path weights/sjy_fused_static.onnx \
--end2end

pip install -U spo4onnx onnx2tf

cd weights
spo4onnx -if sjy_fused_static.onnx -of sjy_fused_static_spo.onnx

onnx2tf -i sjy_fused_static_spo.onnx

image

16. Conversion to TensorFlow.js

When converting to TensorFlow.js, process as follows.

pip install tensorflowjs

onnx2tf -i mobilenetv2-12.onnx -ois input:1,3,224,224 -osd

tensorflowjs_converter \
--input_format tf_saved_model \
--output_format tfjs_graph_model \
saved_model \
tfjs_model

See: https://github.com/tensorflow/tfjs/tree/master/tfjs-converter

image

17. Conversion to CoreML

When converting to CoreML, process as follows. The -k option is for conversion while maintaining the input channel order in ONNX's NCHW format.

pip install coremltools

onnx2tf -i mobilenetv2-12.onnx -k input -ois input:1,3,224,224 -osd
import coremltools as ct

FOLDER_PATH = 'saved_model'

model = ct.convert(
    model=FOLDER_PATH,
    source='tensorflow',
)
model.save(f'{FOLDER_PATH}/model.mlmodel')

See: https://github.com/apple/coremltools

image

CLI Parameter


onnx2tf -h

usage: onnx2tf
[-h]
(-i INPUT_ONNX_FILE_PATH | -V)
[-o OUTPUT_FOLDER_PATH]
[-osd]
[-oh5]
[-okv3]
[-otfv1pb]
[-ow]
[-coion]
[-oiqt]
[-qt {per-channel,per-tensor}]
[-cind INPUT_NAME NUMPY_FILE_PATH MEAN STD]
[-ioqd {int8,uint8}]
[-nuo]
[-nuonag]
[-b BATCH_SIZE]
[-ois OVERWRITE_INPUT_SHAPE [OVERWRITE_INPUT_SHAPE ...]]
[-nlt]
[-onwdt]
[-k KEEP_NCW_OR_NCHW_OR_NCDHW_INPUT_NAMES [KEEP_NCW_OR_NCHW_OR_NCDHW_INPUT_NAMES ...]]
[-kt KEEP_NWC_OR_NHWC_OR_NDHWC_INPUT_NAMES [KEEP_NWC_OR_NHWC_OR_NDHWC_INPUT_NAMES ...]]
[-kat KEEP_SHAPE_ABSOLUTELY_INPUT_NAMES [KEEP_SHAPE_ABSOLUTELY_INPUT_NAMES ...]]
[-onimc OUTPUT_NAMES [OUTPUT_NAMES ...]]
[-dgc]
[-eatfp16]
[-ebu]
[-eru]
[-dsft]
[-nodaftc]
[-dsfs]
[-dsm]
[-nodafsc]
[-ofgd]
[-rari64 | -rarf32 | -rafi64 | -raff32]
[-fasr FUSED_ARGMAX_SCALE_RATIO]
[-rtpo REPLACE_TO_PSEUDO_OPERATORS [REPLACE_TO_PSEUDO_OPERATORS ...]]
[-me MVN_EPSILON]
[-prf PARAM_REPLACEMENT_FILE]
[-cgdc]
[-coto | -cotof]
[-coton]
[-cotor CHECK_ONNX_TF_OUTPUTS_ELEMENTWISE_CLOSE_RTOL]
[-cotoa CHECK_ONNX_TF_OUTPUTS_ELEMENTWISE_CLOSE_ATOL]
[-dms]
[-uc]
[-n]
[-v]

optional arguments:
  -h, --help
    show this help message and exit

  -i INPUT_ONNX_FILE_PATH, --input_onnx_file_path INPUT_ONNX_FILE_PATH
    Input onnx file path.

  -V, --version
    Show version and exit.

  -o OUTPUT_FOLDER_PATH, --output_folder_path OUTPUT_FOLDER_PATH
    Output folder path. Default: "saved_model"

  -osd, --output_signaturedefs
    Signature is added to the output for serving or for conversion
    to other model formats. However, this can significantly reduce the speed
    of model conversion and significant increase the size of the model.

  -oh5, --output_h5
    Output model in Keras (hdf5) format.

  -okv3, --output_keras_v3
    Output model in Keras (keras_v3) format.

  -otfv1pb, --output_tfv1_pb
    Output model in TF v1 (.pb) format.

  -ow, --output_weights
    Output weights in hdf5 format.

  -coion, --copy_onnx_input_output_names_to_tflite
    Copy the input/output OP name of ONNX to the input/output OP name of tflite.
    Due to Tensorflow internal operating specifications,
    the input/output order of ONNX does not necessarily match
    the input/output order of tflite.
    Be sure to check that the input/output OP names in the generated
    tflite file have been converted as expected.
    Also, this option generates a huge JSON file as a temporary file for processing.
    Therefore, it is strongly discouraged to use it on large models of hundreds
    of megabytes or more.

  -oiqt, --output_integer_quantized_tflite
    Output of integer quantized tflite.

  -qt {per-channel,per-tensor}, --quant_type {per-channel,per-tensor}
    Selects whether "per-channel" or "per-tensor" quantization is used.
    Default: "per-channel"

  -cind INPUT_NAME NUMPY_FILE_PATH MEAN STD, \
    --custom_input_op_name_np_data_path INPUT_NAME NUMPY_FILE_PATH MEAN STD
    Input name of OP and path of data file (Numpy) for custom input for -cotof or -oiqt,
    and mean (optional) and std (optional).

    <Usage in -cotof>
      When using -cotof, custom input defined by the user, instead of dummy data, is used.
      In this case, mean and std are omitted from the input.
      -cind {input_op_name} {numpy_file_path}
      e.g. -cind onnx::Equal_0 test_cind/x_1.npy -cind onnx::Add_1 test_cind/x_2.npy -cotof
      The input_op_name must be the same as in ONNX,
      and it may not work if the input format is different between ONNX and TF.

    <Usage in -oiqt>
      INPUT Name of OP and path of calibration data file (Numpy) for quantization
      and mean and std.
      The specification can be omitted only when the input OP is a single 4D tensor image data.
      If omitted, it is automatically calibrated using 20 normalized MS-COCO images.
      The type of the input OP must be Float32.
      Data for calibration must be pre-normalized to a range of 0 to 1.
      -cind {input_op_name} {numpy_file_path} {mean} {std}
      Numpy file paths must be specified the same number of times as the number of input OPs.
      Normalize the value of the input OP based on the tensor specified in mean and std.
      (input_value - mean) / std
      Tensors in Numpy file format must be in dimension order after conversion to TF.
      Note that this is intended for deployment on low-resource devices,
      so the batch size is limited to 1 only.

      e.g.
      The example below shows a case where there are three input OPs.
      Assume input0 is 128x128 RGB image data.
      In addition, input0 should be a value that has been divided by 255
      in the preprocessing and normalized to a range between 0 and 1.
      input1 and input2 assume the input of something that is not an image.
      Because input1 and input2 assume something that is not an image,
      the divisor is not 255 when normalizing from 0 to 1.
      "n" is the number of calibration data.

      ONNX INPUT shapes:
        input0: [n,3,128,128]
            mean: [1,3,1,1] -> [[[[0.485]],[[0.456]],[[0.406]]]]
            std:  [1,3,1,1] -> [[[[0.229]],[[0.224]],[[0.225]]]]
        input1: [n,64,64]
            mean: [1,64] -> [0.1, ..., 0.64]
            std:  [1,64] -> [0.05, ..., 0.08]
        input2: [n,5]
            mean: [1] -> [0.3]
            std:  [1] -> [0.07]
      TensorFlow INPUT shapes (Numpy file ndarray shapes):
        input0: [n,128,128,3]
            mean: [1,1,1,3] -> [[[[0.485, 0.456, 0.406]]]]
            std:  [1,1,1,3] -> [[[[0.229, 0.224, 0.225]]]]
        input1: [n,64,64]
            mean: [1,64] -> [0.1, ..., 0.64]
            std:  [1,64] -> [0.05, ..., 0.08]
        input2: [n,5]
            mean: [1] -> [0.3]
            std:  [1] -> [0.07]
      -cind "input0" "../input0.npy" "[[[[0.485,0.456,0.406]]]]" "[[[[0.229,0.224,0.225]]]]"
      -cind "input1" "./input1.npy" "[0.1,...,0.64]" "[0.05,...,0.08]"
      -cind "input2" "input2.npy" "[0.3]" "[0.07]"

    <Using -cotof and -oiqt at the same time>
      To use -cotof and -oiqt simultaneously,
      you need to enter the Input name of OP, path of data file, mean, and std all together.
      And the data file must be in Float32 format,
      and {input_op_name}, {numpy_file_path}, {mean}, and {std} must all be entered.
      Otherwise, an error will occur during the -oiqt stage.

  -ioqd {int8,uint8}, --input_output_quant_dtype {int8,uint8}
    Input and Output dtypes when doing Full INT8 Quantization.
    "int8"(default) or "uint8"

  -nuo, --not_use_onnxsim
    No optimization by onnx-simplifier is performed.
    If this option is used, the probability of a conversion error is very high.

  -nuonag, --not_use_opname_auto_generate
    Automatic generation of each OP name in the old format ONNX file
    and assignment of OP name are not performed.

  -b BATCH_SIZE, --batch_size BATCH_SIZE
    Fixes the dynamic batch size to the specified numeric batch size.
    A value of 1 or more must be specified.

  -ois OVERWRITE_INPUT_SHAPE [OVERWRITE_INPUT_SHAPE ...], \
      --overwrite_input_shape OVERWRITE_INPUT_SHAPE [OVERWRITE_INPUT_SHAPE ...]
    Overwrite the input shape.
    The format is
    "i1:dim0,...,dimN" "i2:dim0,...,dimN" "i3:dim0,...,dimN"
    When there is only one input, for example,
    "data:1,3,224,224"
    When there are multiple inputs, for example,
    "data1:1,3,224,224" "data2:1,3,112" "data3:5"
    A value of 1 or more must be specified.
    Numerical values other than dynamic dimensions are ignored.
    Ignores --batch_size if specified at the same time as --batch_size.

  -nlt, --no_large_tensor
    Suppresses constant bloat caused by Tile OP when optimizing models in onnxsim.
    See: https://github.com/daquexian/onnx-simplifier/issues/178

  -onwdt, --output_nms_with_dynamic_tensor
    The number of bounding boxes in the NMS output results is
    not fixed at the maximum number of max_output_boxes_per_class,
    but rather at the smallest possible number of dynamic tensors.
    If this option is disabled, NMS output is padded to the number
    set in the max_output_boxes_per_class attribute.
    e.g.
    disable --output_nms_with_dynamic_tensor:
        output_tensor_shape: [100, 7]
    enable --output_nms_with_dynamic_tensor:
        output_tensor_shape: [N, 7]

  -k KEEP_NCW_OR_NCHW_OR_NCDHW_INPUT_NAMES [KEEP_NCW_OR_NCHW_OR_NCDHW_INPUT_NAMES ...], \
      --keep_ncw_or_nchw_or_ncdhw_input_names KEEP_NCW_OR_NCHW_OR_NCDHW_INPUT_NAMES \
          [KEEP_NCW_OR_NCHW_OR_NCDHW_INPUT_NAMES ...]
    Holds the NCW or NCHW or NCDHW of the input shape for the specified INPUT OP names.
    If a nonexistent INPUT OP name is specified, it is ignored.
    Valid only for 3D, 4D and 5D input tensors.
    e.g. --keep_ncw_or_nchw_or_ncdhw_input_names "input0" "input1" "input2"

  -kt KEEP_NWC_OR_NHWC_OR_NDHWC_INPUT_NAMES [KEEP_NWC_OR_NHWC_OR_NDHWC_INPUT_NAMES ...], \
      --keep_nwc_or_nhwc_or_ndhwc_input_names KEEP_NWC_OR_NHWC_OR_NDHWC_INPUT_NAMES \
          [KEEP_NWC_OR_NHWC_OR_NDHWC_INPUT_NAMES ...]
    Holds the NWC or NHWC or NDHWC of the input shape for the specified INPUT OP names.
    If a nonexistent INPUT OP name is specified, it is ignored.
    If the input OP name is the same as the input OP name specified
    in the keep_ncw_or_nchw_or_ncdhw_input_names option, it is ignored.
    Valid only for 3D, 4D and 5D input tensors.
    e.g. --keep_nwc_or_nhwc_or_ndhwc_input_names "input0" "input1" "input2"

  -kat KEEP_SHAPE_ABSOLUTELY_INPUT_NAMES [KEEP_SHAPE_ABSOLUTELY_INPUT_NAMES ...], \
      --keep_shape_absolutely_input_names KEEP_SHAPE_ABSOLUTELY_INPUT_NAMES \
        [KEEP_SHAPE_ABSOLUTELY_INPUT_NAMES ...]
    Name of the INPUT that unconditionally maintains its shape.
    If a nonexistent INPUT OP name is specified, it is ignored.
    e.g. --keep_shape_absolutely_input_names "input0" "input1" "input2"

  -onimc OUTPUT_NAMES [OUTPUT_NAMES ...], \
      --output_names_to_interrupt_model_conversion OUTPUT_NAMES [OUTPUT_NAMES ...]
    Output names of ONNX that interrupt model conversion.
    Interrupts model transformation at the specified output name and outputs the
    model partitioned into subgraphs.
    e.g. --output_names_to_interrupt_model_conversion "output0" "output1" "output2"

  -dgc, --disable_group_convolution
    Disable GroupConvolution and replace it with SeparableConvolution for
    output to saved_model format.

  -eatfp16, --enable_accumulation_type_float16 ENABLE_ACCUMULATION_TYPE_FLOAT16
    Hint for XNNPACK fp16 inference on float16 tflite model.
    XNNPACK float16 inference on certain ARM64 cores is 2x faster.
    Float16 inference doubling on devices with ARM64 ARMv8.2 or higher instruction set.
    See: https://github.com/PINTO0309/onnx2tf/pull/553

  -ebu, --enable_batchmatmul_unfold
    BatchMatMul is separated batch by batch to generate a primitive MatMul.

  -eru, --enable_rnn_unroll
    Instead of increasing inference speed by expanding all symbolic loops of
    the RNN (LSTM, GRU, RNN), RAM consumption will increase because all tensors
    are expanded and embedded in the model.
    https://keras.io/api/layers/recurrent_layers/

  -dsft, --disable_suppression_flextranspose
    Disables FlexTranspose generation suppression.

  -nodaftc, --number_of_dimensions_after_flextranspose_compression
    Number of Transpose OP dimensions generated after avoiding FlexTranspose generation.
    Also suppress the creation of the Transpose itself by specifying 2.
    Default: 6

  -dsfs, --disable_suppression_flexstridedslice
    Disables FlexStridedSlice generation suppression.

  -dsm, --disable_strict_mode
    If specified, the conversion speed is greatly accelerated because the strict accuracy
    correction process is skipped, but the frequency of transposition errors increases
    and accuracy errors are more likely to occur. Strict mode is enabled by default.
    As of 2023.05.07, this is a work in progress and is an experimental feature.
    Therefore, only some OPs are converted in strict mode for accuracy correction.

  -nodafsc, --number_of_dimensions_after_flexstridedslice_compression
    Number of StridedSlice OP dimensions generated after avoiding FlexStridedSlice generation.
    Default: 5

  -ofgd, --optimization_for_gpu_delegate
    Replace operations that do not support gpu delegate with those
    that do as much as possible.

  -rari64, --replace_argmax_to_reducemax_and_indicies_is_int64
    Replace ArgMax with a ReduceMax. The returned indicies are int64.
    Only one of replace_argmax_to_reducemax_and_indicies_is_int64
    and replace_argmax_to_reducemax_and_indicies_is_float32
    and replace_argmax_to_fused_argmax_and_indicies_is_int64
    and replace_argmax_to_fused_argmax_and_indicies_is_float32 can be specified.

  -rarf32, --replace_argmax_to_reducemax_and_indicies_is_float32
    Replace ArgMax with a ReduceMax. The returned indicies are float32.
    Only one of replace_argmax_to_reducemax_and_indicies_is_int64
    and replace_argmax_to_reducemax_and_indicies_is_float32
    and replace_argmax_to_fused_argmax_and_indicies_is_int64
    and replace_argmax_to_fused_argmax_and_indicies_is_float32 can be specified.

  -rafi64, --replace_argmax_to_fused_argmax_and_indicies_is_int64
    Replace ArgMax with a Fused_ArgMax. The returned indicies are int64.
    It improves inference speed at the cost of a small sacrifice in accuracy.
    See. https://github.com/tensorflow/models/tree/master/official/projects/edgetpu/vision#argmax-fusion-to-improve-segmentation-model-latency
    Currently, only 4D tensors are supported.
    Only one of replace_argmax_to_reducemax_and_indicies_is_int64
    and replace_argmax_to_reducemax_and_indicies_is_float32
    and replace_argmax_to_fused_argmax_and_indicies_is_int64
    and replace_argmax_to_fused_argmax_and_indicies_is_float32 can be specified.

  -raff32, --replace_argmax_to_fused_argmax_and_indicies_is_float32
    Replace ArgMax with a Fused_ArgMax. The returned indicies are float32.
    It improves inference speed at the cost of a small sacrifice in accuracy.
    See. https://github.com/tensorflow/models/tree/master/official/projects/edgetpu/vision#argmax-fusion-to-improve-segmentation-model-latency
    Currently, only 4D tensors are supported.
    Only one of replace_argmax_to_reducemax_and_indicies_is_int64
    and replace_argmax_to_reducemax_and_indicies_is_float32
    and replace_argmax_to_fused_argmax_and_indicies_is_int64
    and replace_argmax_to_fused_argmax_and_indicies_is_float32 can be specified.

  -fasr FUSED_ARGMAX_SCALE_RATIO, --fused_argmax_scale_ratio FUSED_ARGMAX_SCALE_RATIO
    For Fused ArgMax.
    Scale ratio when generating Fused ArgMax.
    0.0 < fused_argmax_scale_ratio <= 1.0
    Default: 0.5

  -rtpo, --replace_to_pseudo_operators
    Replace list of operators to pseudo operators.
    Full name of the target operators should be given.
    Currently supported operators :
    Asin, Acos, Atan, Abs, PReLU, LeakyReLU, Power, GatherND, Neg, HardSwish, Erf, GeLU

  -me, --mvn_epsilon
    For MeanVarianceNormalization.
    The number to be added to the variance to avoid division by zero
    when normalizing the value.
    (input_tensor - mean) / tf.sqrt(variance + mvn_epsilon)
    Default: 0.0000000001

  -prf PARAM_REPLACEMENT_FILE, --param_replacement_file PARAM_REPLACEMENT_FILE
    Parameter replacement file path. (.json)

  -cgdc, --check_gpu_delegate_compatibility
    Run TFLite ModelAnalyzer on the generated Float16 tflite model
    to check if the model can be supported by GPU Delegate.
    e.g.
    """
    === TFLite ModelAnalyzer ===

    Your TFLite model has '1' subgraph(s). In the subgraph description below,
    T# represents the Tensor numbers. For example, in Subgraph#0, the RESHAPE op takes
    tensor #0 and tensor #6 as input and produces tensor #7 as output.

    Subgraph#0 main(T#0) -> [T#17]
      Op#0 RESHAPE(T#0, T#6[2, 8, 8, 3, 2, ...]) -> [T#7]
      Op#1 SPLIT(T#5[0], T#7) -> [T#8, T#9]
      Op#2 RESHAPE(T#8, T#1[8, 8, 3, 2, 2]) -> [T#10]
      Op#3 TRANSPOSE(T#10, T#4[0, 3, 1, 4, 2]) -> [T#11]
      Op#4 RESHAPE(T#11, T#2[1, 8, 2, 8, 2, ...]) -> [T#12]
      Op#5 RESHAPE(T#9, T#1[8, 8, 3, 2, 2]) -> [T#13]
      Op#6 TRANSPOSE(T#13, T#4[0, 3, 1, 4, 2]) -> [T#14]
      Op#7 RESHAPE(T#14, T#2[1, 8, 2, 8, 2, ...]) -> [T#15]
      Op#8 CONCATENATION(T#12, T#15) -> [T#16]
      Op#9 RESHAPE(T#16, T#3[2, 16, 16, 3]) -> [T#17]

    Tensors of Subgraph#0
      T#0(inputs_0) shape:[2, 8, 8, 12], type:FLOAT32
      T#1(model/tf.compat.v1.squeeze_2/Squeeze) shape:[5], type:INT32 RO 20 bytes, data:[8, 8, 3, 2, 2]
      T#2(model/tf.expand_dims_1/ExpandDims) shape:[6], type:INT32 RO 24 bytes, data:[1, 8, 2, 8, 2, ...]
      T#3(model/tf.reshape_1/Reshape/shape) shape:[4], type:INT32 RO 16 bytes, data:[2, 16, 16, 3]
      T#4(model/tf.compat.v1.transpose/transpose/perm) shape:[5], type:INT32 RO 20 bytes, data:[0, 3, 1, 4, 2]
      T#5(model/tf.concat/concat/axis) shape:[], type:INT32 RO 4 bytes, data:[0]
      T#6(model/tf.reshape/Reshape/shape) shape:[6], type:INT32 RO 24 bytes, data:[2, 8, 8, 3, 2, ...]
      T#7(model/tf.reshape/Reshape) shape:[2, 8, 8, 3, 2, 2], type:FLOAT32
      T#8(model/tf.split/split) shape:[1, 8, 8, 3, 2, 2], type:FLOAT32
      T#9(model/tf.split/split1) shape:[1, 8, 8, 3, 2, 2], type:FLOAT32
      T#10(model/tf.compat.v1.squeeze_1/Squeeze) shape:[8, 8, 3, 2, 2], type:FLOAT32
      T#11(model/tf.compat.v1.transpose/transpose) shape:[8, 2, 8, 2, 3], type:FLOAT32
      T#12(model/tf.expand_dims/ExpandDims) shape:[1, 8, 2, 8, 2, 3], type:FLOAT32
      T#13(model/tf.compat.v1.squeeze_2/Squeeze1) shape:[8, 8, 3, 2, 2], type:FLOAT32
      T#14(model/tf.compat.v1.transpose_1/transpose) shape:[8, 2, 8, 2, 3], type:FLOAT32
      T#15(model/tf.expand_dims_1/ExpandDims1) shape:[1, 8, 2, 8, 2, 3], type:FLOAT32
      T#16(model/tf.concat/concat) shape:[2, 8, 2, 8, 2, 3], type:FLOAT32
      T#17(Identity) shape:[2, 16, 16, 3], type:FLOAT32

    Your model looks compatibile with GPU delegate with TFLite runtime version 2.10.0.
    But it doesn't guarantee that your model works well with GPU delegate.
    There could be some runtime incompatibililty happen.
    ---------------------------------------------------------------
                  Model size:       2988 bytes
        Non-data buffer size:       2757 bytes (92.27 %)
      Total data buffer size:        231 bytes (07.73 %)
        (Zero value buffers):          4 bytes (00.13 %)

    * Buffers of TFLite model are mostly used for constant tensors.
      And zero value buffers are buffers filled with zeros.
      Non-data buffers area are used to store operators, subgraphs and etc.
      You can find more details from https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/schema/schema.fbs
    """

  -coto, --check_onnx_tf_outputs_elementwise_close
    Returns "Matches" if the output of onnx and the output of TF are
    within acceptable proximity element by element.
    Returns "Unmatched" if the output of onnx and the output of TF are
    not within acceptable proximity element by element.
    If the output of onnx is 1D, it returns "Skipped" and skips the comparison
    between the output of onnx and that of TF. This is because when undefined
    dimensions are present, a situation often arises where very large index
    values are compared, causing OutOfMemory.
    Only the output content of the models final output OP is checked.

  -cotof, --check_onnx_tf_outputs_elementwise_close_full
    Returns "Matches" if the output of onnx and the output of TF are
    within acceptable proximity element by element.
    Check the output of all OPs in sequence from the beginning,
    including all but the final output OP of the model.
    Returns "Unmatched" if the output of onnx and the output of TF are
    not within acceptable proximity element by element.
    If the output of onnx is 1D, it returns "Skipped" and skips the comparison
    between the output of onnx and that of TF. This is because when undefined
    dimensions are present, a situation often arises where very large index
    values are compared, causing OutOfMemory.
    It is very time consuming because it performs as many inferences as
    there are operations.

  -coton, --check_onnx_tf_outputs_sample_data_normalization
    norm: Validate using random data normalized to the range 0.0 to 1.0
    denorm: Validate using random data in the range 0.0 to 255.0
    If there is a normalization layer at the model's entry point, or
    if the model was trained on denormalized data, "denorm" must be specified.
    Default: "norm"

  -cotor CHECK_ONNX_TF_OUTPUTS_ELEMENTWISE_CLOSE_RTOL,\
    --check_onnx_tf_outputs_elementwise_close_rtol CHECK_ONNX_TF_OUTPUTS_ELEMENTWISE_CLOSE_RTOL
    The relative tolerance parameter.
    Default: 0.0

  -cotoa CHECK_ONNX_TF_OUTPUTS_ELEMENTWISE_CLOSE_ATOL,\
    --check_onnx_tf_outputs_elementwise_close_atol CHECK_ONNX_TF_OUTPUTS_ELEMENTWISE_CLOSE_ATOL
    The absolute tolerance parameter.
    Default: 1e-4

  -dms, --disable_model_save
    Does not save the converted model. For CIs RAM savings.

  -n, --non_verbose
    Shorthand to specify a verbosity of "error".

  -v, --verbosity
    Change the level of information printed.
    Values are "debug", "info", "warn", and "error".
    Default: "debug" (for backwards compatability)

In-script Usage

>>> from onnx2tf import convert
>>> help(convert)

Help on function convert in module onnx2tf:

convert(
  input_onnx_file_path: Union[str, NoneType] = '',
  onnx_graph: Union[onnx.onnx_ml_pb2.ModelProto, NoneType] = None,
  output_folder_path: Union[str, NoneType] = 'saved_model',
  output_signaturedefs: Optional[bool] = False,
  output_h5: Optional[bool] = False,
  output_keras_v3: Optional[bool] = False,
  output_tfv1_pb: Optional[bool] = False,
  output_weights: Optional[bool] = False,
  copy_onnx_input_output_names_to_tflite: Optional[bool] = False,
  output_integer_quantized_tflite: Optional[bool] = False,
  quant_type: Optional[str] = 'per-channel',
  custom_input_op_name_np_data_path: Optional[List] = None,
  input_output_quant_dtype: Optional[str] = 'int8',
  not_use_onnxsim: Optional[bool] = False,
  not_use_opname_auto_generate: Optional[bool] = False,
  batch_size: Union[int, NoneType] = None,
  overwrite_input_shape: Union[List[str], NoneType] = None,
  no_large_tensor: Optional[bool] = False,
  output_nms_with_dynamic_tensor: Optional[bool] = False,
  keep_ncw_or_nchw_or_ncdhw_input_names: Union[List[str], NoneType] = None,
  keep_nwc_or_nhwc_or_ndhwc_input_names: Union[List[str], NoneType] = None,
  keep_shape_absolutely_input_names: Optional[List[str]] = None,
  output_names_to_interrupt_model_conversion: Union[List[str], NoneType] = None,
  disable_group_convolution: Union[bool, NoneType] = False,
  enable_batchmatmul_unfold: Optional[bool] = False,
  enable_rnn_unroll: Optional[bool] = False,
  disable_suppression_flextranspose: Optional[bool] = False,
  number_of_dimensions_after_flextranspose_compression: Optional[int] = 6,
  disable_suppression_flexstridedslice: Optional[bool] = False,
  disable_strict_mode: Optional[bool] = False,
  number_of_dimensions_after_flexstridedslice_compression: Optional[int] = 5,
  optimization_for_gpu_delegate: Optional[bool] = False,
  replace_argmax_to_reducemax_and_indicies_is_int64: Union[bool, NoneType] = False,
  replace_argmax_to_reducemax_and_indicies_is_float32: Union[bool, NoneType] = False,
  replace_argmax_to_fused_argmax_and_indicies_is_int64: Union[bool, NoneType] = False,
  replace_argmax_to_fused_argmax_and_indicies_is_float32: Union[bool, NoneType] = False,
  fused_argmax_scale_ratio: Union[float, NoneType] = 0.5,
  replace_to_pseudo_operators: List[str] = None,
  mvn_epsilon: Union[float, NoneType] = 0.0000000001,
  param_replacement_file: Optional[str] = '',
  check_gpu_delegate_compatibility: Optional[bool] = False,
  check_onnx_tf_outputs_elementwise_close: Optional[bool] = False,
  check_onnx_tf_outputs_elementwise_close_full: Optional[bool] = False,
  check_onnx_tf_outputs_sample_data_normalization: Optional[str] = 'norm',
  check_onnx_tf_outputs_elementwise_close_rtol: Optional[float] = 0.0,
  check_onnx_tf_outputs_elementwise_close_atol: Optional[float] = 1e-4,
  disable_model_save: Union[bool, NoneType] = False,
  non_verbose: Union[bool, NoneType] = False,
  verbosity: Optional[str] = 'debug'
) -> keras.engine.training.Model

    Convert ONNX to TensorFlow models.

    Parameters
    ----------
    input_onnx_file_path: Optional[str]
      Input onnx file path.
      Either input_onnx_file_path or onnx_graph must be specified.

    onnx_graph: Optional[onnx.ModelProto]
      onnx.ModelProto.
      Either input_onnx_file_path or onnx_graph must be specified.
      onnx_graph If specified, ignore input_onnx_file_path and process onnx_graph.

    output_folder_path: Optional[str]
      Output tensorflow model folder path.
      Default: "saved_model"

    output_signaturedefs: Optional[bool]
      Signature is added to the output for serving or for conversion
      to other model formats. However, this can significantly reduce the speed
      of model conversion and significant increase the size of the model.

    output_h5: Optional[bool]
      Output model in Keras H5 format.

    output_keras_v3: Optional[bool]
      Output model in Keras (keras_v3) format.

    output_tfv1_pb: Optional[bool]
      Output model in TF v1 (.pb) format.

    output_weights: Optional[bool]
      Output weights in hdf5 format.

    copy_onnx_input_output_names_to_tflite: Optional[bool]
      Copy the input/output OP name of ONNX to the input/output OP name of tflite.
      Due to Tensorflow internal operating specifications,
      the input/output order of ONNX does not necessarily match
      the input/output order of tflite.
      Be sure to check that the input/output OP names in the generated
      tflite file have been converted as expected.
      Also, this option generates a huge JSON file as a temporary file for processing.
      Therefore, it is strongly discouraged to use it on large models of hundreds
      of megabytes or more.

    output_integer_quantized_tflite: Optional[bool]
      Output of integer quantized tflite.

    quant_type: Optional[str]
      Selects whether "per-channel" or "per-tensor" quantization is used.
      Default: "per-channel"

    custom_input_op_name_np_data_path: Optional[List]
      --custom_input_op_name_np_data_path INPUT_NAME NUMPY_FILE_PATH MEAN STD
      Input name of OP and path of data file (Numpy) for custom input for -cotof or -oiqt,
      and mean (optional) and std (optional).

      <Usage in -cotof>
        When using -cotof, custom input defined by the user, instead of dummy data, is used.
        In this case, mean and std are omitted from the input.
        -cind {input_op_name} {numpy_file_path}
        e.g. -cind onnx::Equal_0 test_cind/x_1.npy -cind onnx::Add_1 test_cind/x_2.npy -cotof
        The input_op_name must be the same as in ONNX,
        and it may not work if the input format is different between ONNX and TF.

      <Usage in -oiqt>
        INPUT Name of OP and path of calibration data file (Numpy) for quantization
        and mean and std.
        The specification can be omitted only when the input OP is a single 4D tensor image data.
        If omitted, it is automatically calibrated using 20 normalized MS-COCO images.
        The type of the input OP must be Float32.
        Data for calibration must be pre-normalized to a range of 0 to 1.
        -cind {input_op_name} {numpy_file_path} {mean} {std}
        Numpy file paths must be specified the same number of times as the number of input OPs.
        Normalize the value of the input OP based on the tensor specified in mean and std.
        (input_value - mean) / std
        Tensors in Numpy file format must be in dimension order after conversion to TF.
        Note that this is intended for deployment on low-resource devices,
        so the batch size is limited to 1 only.
        e.g.
        The example below shows a case where there are three input OPs.
        Assume input0 is 128x128 RGB image data.
        In addition, input0 should be a value that has been divided by 255
        in the preprocessing and normalized to a range between 0 and 1.
        input1 and input2 assume the input of something that is not an image.
        Because input1 and input2 assume something that is not an image,
        the divisor is not 255 when normalizing from 0 to 1.
        "n" is the number of calibration data.

        ONNX INPUT shapes:
          input0: [n,3,128,128]
            mean: [1,3,1,1] -> [[[[0.485]],[[0.456]],[[0.406]]]]
            std : [1,3,1,1] -> [[[[0.229]],[[0.224]],[[0.225]]]]
          input1: [n,64,64]
            mean: [1,64] -> [[0.1, ..., 0.64]]
            std : [1,64] -> [[0.05, ..., 0.08]]
          input2: [n,5]
            mean: [1] -> [0.3]
            std : [1] -> [0.07]

        TensorFlow INPUT shapes (Numpy file ndarray shapes):
          input0: [n,128,128,3]
            mean: [1,1,1,3] -> [[[[0.485, 0.456, 0.406]]]]
            std : [1,1,1,3] -> [[[[0.229, 0.224, 0.225]]]]
          input1: [n,64,64]
            mean: [1,64] -> [[0.1, ..., 0.64]]
            std : [1,64] -> [[0.05, ..., 0.08]]
          input2: [n,5]
            mean: [1] -> [0.3]
            std : [1] -> [0.07]

          cind=[
              ["input0","../input0.npy",[[[[0.485, 0.456, 0.406]]]],[[[[0.229, 0.224, 0.225]]]]],
              ["input1","./input1.npy",[0.1, ..., 0.64],[0.05, ..., 0.08]],
              ["input2","input2.npy",[0.3],[0.07]],
          ]

      <Using -cotof and -oiqt at the same time>
        To use -cotof and -oiqt simultaneously,
        you need to enter the Input name of OP, path of data file, mean, and std all together.
        And the data file must be in Float32 format,
        and {input_op_name}, {numpy_file_path}, {mean}, and {std} must all be entered.
        Otherwise, an error will occur during the -oiqt stage.

    input_output_quant_dtype: Optional[str]
      Input and Output dtypes when doing Full INT8 Quantization.
      "int8"(default) or "uint8"

    not_use_onnxsim: Optional[bool]
      No optimization by onnx-simplifier is performed.
      If this option is used, the probability of a conversion error is very high.

    not_use_opname_auto_generate: Optional[bool]
      Automatic generation of each OP name in the old format ONNX file
      and assignment of OP name are not performed.

    batch_size: Optional[int]
      Fixes the dynamic batch size to the specified numeric batch size.
      A value of 1 or more must be specified.

    overwrite_input_shape: Optional[List[str]]
      Overwrite the input shape.
      The format is
      ['i1:dim0,dim1,...,dimN', 'i2:dim0,dim1,...,dimN', 'i3:dim0,dim1,...,dimN']
      When there is only one input, for example,
      ['data:1,3,224,224']
      When there are multiple inputs, for example,
      ['data1:1,3,224,224','data2:1,3,112','data3:5']
      A value of 1 or more must be specified.
      Numerical values other than dynamic dimensions are ignored.
      Ignores batch_size if specified at the same time as batch_size.

    no_large_tensor: Optional[bool]
      Suppresses constant bloat caused by Tile OP when optimizing models in onnxsim.
      See: https://github.com/daquexian/onnx-simplifier/issues/178

    output_nms_with_dynamic_tensor: Optional[bool]
      The number of bounding boxes in the NMS output results is
      not fixed at the maximum number of max_output_boxes_per_class,
      but rather at the smallest possible number of dynamic tensors.
      If this option is disabled, NMS output is padded to the number
      set in the max_output_boxes_per_class attribute.
      e.g.
      disable --output_nms_with_dynamic_tensor:
          output_tensor_shape: [100, 7]
      enable --output_nms_with_dynamic_tensor:
          output_tensor_shape: [N, 7]

    keep_ncw_or_nchw_or_ncdhw_input_names: Optional[List[str]]
      Holds the NCW or NCHW or NCDHW of the input shape for the specified INPUT OP names.
      If a nonexistent INPUT OP name is specified, it is ignored.
      Valid only for 3D, 4D and 5D input tensors.
      e.g.
      keep_ncw_or_nchw_or_ncdhw_input_names=['input0','input1','input2']

    keep_nwc_or_nhwc_or_ndhwc_input_names: Optional[List[str]]
      Holds the NWC or NHWC or NDHWC of the input shape for the specified INPUT OP names.
      If a nonexistent INPUT OP name is specified, it is ignored.
      If the input OP name is the same as the input OP name specified
      in the keep_ncw_or_nchw_or_ncdhw_input_names option, it is ignored.
      Valid only for 3D, 4D and 5D input tensors.
      e.g.
      keep_nwc_or_nhwc_or_ndhwc_input_names=['input0','input1','input2']

    keep_shape_absolutely_input_names: Optional[List[str]]
      Name of the INPUT that unconditionally maintains its shape.
      If a nonexistent INPUT OP name is specified, it is ignored.
      e.g.
      keep_shape_absolutely_input_names=['input0','input1','input2']

    output_names_to_interrupt_model_conversion: Optional[List[str]]
      Output names of ONNX that interrupt model conversion.
      Interrupts model transformation at the specified output name
      and outputs the model partitioned into subgraphs.
      e.g.
      output_names_to_interrupt_model_conversion=['output0','output1','output2']

    disable_group_convolution: Optional[bool]
      Disable GroupConvolution and replace it with SeparableConvolution for
      output to saved_model format.

    enable_accumulation_type_float16: Optional[bool]
      Hint for XNNPack fp16 inference on float16 tflite model.
      XNNPACK float16 inference on certain ARM64 cores is 2x faster.
      Float16 inference doubling on devices with ARM64 ARMv8.2 or higher instruction set.
      https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/delegates/xnnpack/README.md#floating-point-ieee-fp16-operators

    enable_batchmatmul_unfold: Optional[bool]
      BatchMatMul is separated batch by batch to generate a primitive MatMul.

    enable_rnn_unroll: Optional[bool]
      Instead of increasing inference speed by expanding all symbolic loops of
      the RNN (LSTM, GRU, RNN), RAM consumption will increase because all tensors
      are expanded and embedded in the model.
      https://keras.io/api/layers/recurrent_layers/

    disable_suppression_flextranspose: Optional[bool]
      Disables FlexTranspose generation suppression.

    number_of_dimensions_after_flextranspose_compression: Optional[int]
      Number of Transpose OP dimensions generated after avoiding FlexTranspose generation.
      Also suppress the creation of the Transpose itself by specifying 2.
      Default: 6

    disable_suppression_flexstridedslice: Optional[bool]
      Disables FlexStridedSlice generation suppression.

    disable_strict_mode: Optional[bool]
      If specified, the conversion speed is greatly accelerated because the strict accuracy
      correction process is skipped, but the frequency of transposition errors increases
      and accuracy errors are more likely to occur. Strict mode is enabled by default.
      As of 2023.05.07, this is a work in progress and is an experimental feature.
      Therefore, only some OPs are converted in strict mode for accuracy correction.

    number_of_dimensions_after_flexstridedslice_compression: Optional[int]
      Number of StridedSlice OP dimensions generated after avoiding FlexStridedSlice generation.
      Default: 5

    optimization_for_gpu_delegate: Optional[bool]
      Replace operations that do not support gpu delegate with those
      that do as much as possible.

    replace_argmax_to_reducemax_and_indicies_is_int64: Optional[bool]
      Replace ArgMax with a ReduceMax. The returned indicies are int64.
      Only one of replace_argmax_to_reducemax_and_indicies_is_int64 and
      replace_argmax_to_reducemax_and_indicies_is_float32 and
      replace_argmax_to_fused_argmax_and_indicies_is_int64 and
      replace_argmax_to_fused_argmax_and_indicies_is_float32 can be specified.
      Default: False

    replace_argmax_to_reducemax_and_indicies_is_float32: Optional[bool]
      Replace ArgMax with a ReduceMax. The returned indicies are float32.
      Only one of replace_argmax_to_reducemax_and_indicies_is_int64 and
      replace_argmax_to_reducemax_and_indicies_is_float32 and
      replace_argmax_to_fused_argmax_and_indicies_is_int64 and
      replace_argmax_to_fused_argmax_and_indicies_is_float32 can be specified.
      Default: False

    replace_argmax_to_fused_argmax_and_indicies_is_int64: Optional[bool]
      Replace ArgMax with a ReduceMax. The returned indicies are int64.
      It improves inference speed at the cost of a small sacrifice in accuracy.
      See. https://github.com/tensorflow/models/tree/master/official/projects/edgetpu/vision#argmax-fusion-to-improve-segmentation-model-latency
      Currently, only 4D tensors are supported.
      Only one of replace_argmax_to_reducemax_and_indicies_is_int64 and
      replace_argmax_to_reducemax_and_indicies_is_float32 and
      replace_argmax_to_fused_argmax_and_indicies_is_int64 and
      replace_argmax_to_fused_argmax_and_indicies_is_float32 can be specified.
      Default: False

    replace_argmax_to_fused_argmax_and_indicies_is_float32: Optional[bool]
      Replace ArgMax with a ReduceMax. The returned indicies are float32.
      It improves inference speed at the cost of a small sacrifice in accuracy.
      See. https://github.com/tensorflow/models/tree/master/official/projects/edgetpu/vision#argmax-fusion-to-improve-segmentation-model-latency
      Currently, only 4D tensors are supported.
      Only one of replace_argmax_to_reducemax_and_indicies_is_int64 and
      replace_argmax_to_reducemax_and_indicies_is_float32 and
      replace_argmax_to_fused_argmax_and_indicies_is_int64 and
      replace_argmax_to_fused_argmax_and_indicies_is_float32 can be specified.
      Default: False

    fused_argmax_scale_ratio: Optional[float]
      For Fused ArgMax.
      Scale ratio when generating Fused ArgMax.
      0.0 < fused_argmax_scale_ratio <= 1.0
      Default: 0.5

    replace_to_pseudo_operators: List[str]
      Replace list of operators to pseudo operators.
      Full name of the target operators should be given.
      Currently supported operators :
      Asin, Acos, Atan, Abs, PReLU, LeakyReLU, Power, GatherND, Neg, HardSwish, Erf, GeLU

    mvn_epsilon: Optional[float]
      For MeanVarianceNormalization.
      The number to be added to the variance to avoid division by zero
      when normalizing the value.
      (input_tensor - mean) / tf.sqrt(variance + mvn_epsilon)
      Default: 0.0000000001

    param_replacement_file: Optional[str]
      Parameter replacement file path. (.json)

    check_gpu_delegate_compatibility: Optional[bool]
      Run TFLite ModelAnalyzer on the generated Float16 tflite model
      to check if the model can be supported by GPU Delegate.
      e.g.
      """
      === TFLite ModelAnalyzer ===

      Your TFLite model has '1' subgraph(s). In the subgraph description below,
      T# represents the Tensor numbers. For example, in Subgraph#0, the RESHAPE op takes
      tensor #0 and tensor #6 as input and produces tensor #7 as output.

      Subgraph#0 main(T#0) -> [T#17]
        Op#0 RESHAPE(T#0, T#6[2, 8, 8, 3, 2, ...]) -> [T#7]
        Op#1 SPLIT(T#5[0], T#7) -> [T#8, T#9]
        Op#2 RESHAPE(T#8, T#1[8, 8, 3, 2, 2]) -> [T#10]
        Op#3 TRANSPOSE(T#10, T#4[0, 3, 1, 4, 2]) -> [T#11]
        Op#4 RESHAPE(T#11, T#2[1, 8, 2, 8, 2, ...]) -> [T#12]
        Op#5 RESHAPE(T#9, T#1[8, 8, 3, 2, 2]) -> [T#13]
        Op#6 TRANSPOSE(T#13, T#4[0, 3, 1, 4, 2]) -> [T#14]
        Op#7 RESHAPE(T#14, T#2[1, 8, 2, 8, 2, ...]) -> [T#15]
        Op#8 CONCATENATION(T#12, T#15) -> [T#16]
        Op#9 RESHAPE(T#16, T#3[2, 16, 16, 3]) -> [T#17]

      Tensors of Subgraph#0
        T#0(inputs_0) shape:[2, 8, 8, 12], type:FLOAT32
        T#1(model/tf.compat.v1.squeeze_2/Squeeze) shape:[5], type:INT32 RO 20 bytes, data:[8, 8, 3, 2, 2]
        T#2(model/tf.expand_dims_1/ExpandDims) shape:[6], type:INT32 RO 24 bytes, data:[1, 8, 2, 8, 2, ...]
        T#3(model/tf.reshape_1/Reshape/shape) shape:[4], type:INT32 RO 16 bytes, data:[2, 16, 16, 3]
        T#4(model/tf.compat.v1.transpose/transpose/perm) shape:[5], type:INT32 RO 20 bytes, data:[0, 3, 1, 4, 2]
        T#5(model/tf.concat/concat/axis) shape:[], type:INT32 RO 4 bytes, data:[0]
        T#6(model/tf.reshape/Reshape/shape) shape:[6], type:INT32 RO 24 bytes, data:[2, 8, 8, 3, 2, ...]
        T#7(model/tf.reshape/Reshape) shape:[2, 8, 8, 3, 2, 2], type:FLOAT32
        T#8(model/tf.split/split) shape:[1, 8, 8, 3, 2, 2], type:FLOAT32
        T#9(model/tf.split/split1) shape:[1, 8, 8, 3, 2, 2], type:FLOAT32
        T#10(model/tf.compat.v1.squeeze_1/Squeeze) shape:[8, 8, 3, 2, 2], type:FLOAT32
        T#11(model/tf.compat.v1.transpose/transpose) shape:[8, 2, 8, 2, 3], type:FLOAT32
        T#12(model/tf.expand_dims/ExpandDims) shape:[1, 8, 2, 8, 2, 3], type:FLOAT32
        T#13(model/tf.compat.v1.squeeze_2/Squeeze1) shape:[8, 8, 3, 2, 2], type:FLOAT32
        T#14(model/tf.compat.v1.transpose_1/transpose) shape:[8, 2, 8, 2, 3], type:FLOAT32
        T#15(model/tf.expand_dims_1/ExpandDims1) shape:[1, 8, 2, 8, 2, 3], type:FLOAT32
        T#16(model/tf.concat/concat) shape:[2, 8, 2, 8, 2, 3], type:FLOAT32
        T#17(Identity) shape:[2, 16, 16, 3], type:FLOAT32

      Your model looks compatibile with GPU delegate with TFLite runtime version 2.10.0.
      But it doesn't guarantee that your model works well with GPU delegate.
      There could be some runtime incompatibililty happen.
      ---------------------------------------------------------------
                    Model size:       2988 bytes
          Non-data buffer size:       2757 bytes (92.27 %)
        Total data buffer size:        231 bytes (07.73 %)
          (Zero value buffers):          4 bytes (00.13 %)

      * Buffers of TFLite model are mostly used for constant tensors.
        And zero value buffers are buffers filled with zeros.
        Non-data buffers area are used to store operators, subgraphs and etc.
        You can find more details from https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/schema/schema.fbs
      """

    check_onnx_tf_outputs_elementwise_close: Optional[bool]
      Returns "Matches" if the output of onnx and the output of TF are
      within acceptable proximity element by element.
      Returns "Unmatched" if the output of onnx and the output of TF are
      not within acceptable proximity element by element.
      If the output of onnx is 1D, it returns "Skipped" and skips the comparison
      between the output of onnx and that of TF. This is because when undefined
      dimensions are present, a situation often arises where very large index
      values are compared, causing OutOfMemory.
      Only the output content of the models final output OP is checked.

    check_onnx_tf_outputs_elementwise_close_full: Optional[bool]
      Returns "Matches" if the output of onnx and the output of TF are
      within acceptable proximity element by element.
      Check the output of all OPs in sequence from the beginning,
      including all but the final output OP of the model.
      Returns "Unmatched" if the output of onnx and the output of TF are
      not within acceptable proximity element by element.
      If the output of onnx is 1D, it returns "Skipped" and skips the comparison
      between the output of onnx and that of TF. This is because when undefined
      dimensions are present, a situation often arises where very large index
      values are compared, causing OutOfMemory.
      It is very time consuming because it performs as many inferences as
      there are operations.

    check_onnx_tf_outputs_sample_data_normalization: Optional[str]
      norm: Validate using random data normalized to the range 0.0 to 1.0
      denorm: Validate using random data in the range 0.0 to 255.0
      If there is a normalization layer at the models entry point, or
      if the model was trained on denormalized data, "denorm" must be specified.
      Default: "norm"

    check_onnx_tf_outputs_elementwise_close_rtol: Optional[float]
      The relative tolerance parameter.
      Default: 0.0

    check_onnx_tf_outputs_elementwise_close_atol: Optional[float]
      The absolute tolerance parameter.
      Default: 1e-4

    disable_model_save: Optional[bool]
      Does not save the converted model. For CIs RAM savings.
      Default: False

    non_verbose: Optional[bool]
      Shorthand to specify a verbosity of "error".
      Default: False

    verbosity: Optional[str]
      Change the level of information printed.
      Values are "debug", "info", "warn", and "error".
      Default: "debug" (for backwards compatability)

    Returns
    ----------
    model: tf.keras.Model
      Model

Parameter replacement

This tool is used to convert NCW to NWC, NCHW to NHWC, NCDHW to NDHWC, NCDDHW to NDDHWC, NCDDDDDDHW to NDDDDDDHWC. Therefore, as stated in the Key Concepts, the conversion will inevitably break down at some point in the model. You need to look at the entire conversion log to see which OP transpositions are failing and correct them yourself. I dare to explain very little because I know that no matter how much detail I put in the README, you guys will not read it at all. attribute or INPUT constant or INPUT Initializer can be replaced with the specified value.

Starting from v1.3.0, almost all OPs except for some special OPs support pre- and post-transposition by pre_process_transpose and post_process_transpose.

  1. "A conversion error occurs."
  2. "Output results are wrong."

Do not submit an issue that only contains an amount of information that cannot be reproduced.

  • convert option

    --param_replacement_file param_replacement.json
    
    or
    
    -prf param_replacement.json
    
  • param_replacement.json

    See a sample of replacement JSON
    {
      "format_version": 1,
      "operations": [
        {
          "op_name": "StatefulPartitionedCall/Tile_4",
          "param_target": "inputs", # attributes or inputs
          "param_name": "const_fold_opt__677",
          "values": [1,1,17] # Disable parameter transposition or overwrite parameters
        },
        {
          "op_name": "StatefulPartitionedCall/Cast_3",
          "param_target": "attributes", # attributes or inputs
          "param_name": "to",
          "values": 1 # Disable parameter transposition or overwrite "to" parameters
        },
        {
          "op_name": "Resize__697",
          "param_target": "inputs",
          "param_name": "Concat__696:0",
          "values": [26,26] # Replacement of unk__x (Resize OP, sizes height/width parameter)
        },
        {
          "op_name": "Transpose__927",
          "param_target": "attributes",
          "param_name": "perm",
          "values": [0,1,2,3] # Disable parameter transposition or overwrite "perm" parameters
        },
        {
          "op_name": "StatefulPartitionedCall/functional_1/max_unpooling2d_2/Reshape_1",
          "param_target": "inputs",
          "param_name": "const_fold_opt__911",
          "values": [4,131072] # Overwrite "shape" parameters
        },
        {
          "op_name": "Reshape_25",
          "param_target": "outputs",
          "param_name": "onnx::InstanceNormalization_270",
          "post_process_transpose_perm": [0,2,1] # Extrapolate 3D Transpose after Reshape
        },
        {
          "op_name": "Reshape_30",
          "param_target": "outputs",
          "param_name": "onnx::Mul_275",
          "post_process_transpose_perm": [0,2,3,1] # Extrapolate 4D Transpose after Reshape
        },
        {
          "op_name": "flatten_1127",
          "param_target": "inputs",
          "param_name": "dropout0",
          "pre_process_transpose_perm": [0,3,1,2]
        },
        {
          "op_name": "/Slice",
          "param_target": "op",
          "begin": [0,0,1,0],
          "end": [0,0,0,0],
          "end_mask": 15
        },
        {
          "op_name": "/Slice_1",
          "param_target": "op",
          "begin": [0,0,0,0],
          "end": [0,0,39,0],
          "end_mask": 11
        },
        {
          "op_name": "/backbone/backbone.1/Unsqueeze_1",
          "param_target": "op",
          "new_shape": [1,15,15,1]
        }
      ]
    }
  • Replacement Supported OPs

    See list of replacement specifications
    No. OP type Remarks
    1 Add 1. "param_target": "inputs"
    pre_process_transpose_perm: Transpose is applied to the tensor before the Add operation with the perm specified as pre-processing.
    2. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the Add operation with the perm specified as post-processing.
    2 Cast
    TypeValuesTypeValues
    float1610int83
    float321int165
    float6411int326
    bool9int647
    uint82
    uint164
    uint3212
    uint6413
    3 Concat 1. "param_target": "attributes"
    axis: Value of axis
    2. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the Concat operation with the perm specified as post-processing.
    4 ConvTranspose ConvTranspose implements special replacements separately ignore all automatic conversions and generate tf.nn.conv1d_transpose or tf.nn.conv2d_transpose or tf.nn.conv3d_transpose directly by specifying all parameters.
    https://www.tensorflow.org/api_docs/python/tf/nn/conv1d_transpose
    https://www.tensorflow.org/api_docs/python/tf/nn/conv2d_transpose
    https://www.tensorflow.org/api_docs/python/tf/nn/conv3d_transpose
    1. "param_target": "op"
    output_shape: Value of output_shape
    strides: Value of strides
    padding: Value of padding
    dilations: Value of dilations
    5 Div 1. "param_target": "inputs"
    values: Value of input
    pre_process_transpose_perm: Transpose is applied to the tensor before the Div operation with the perm specified as pre-processing.
    2. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the Div operation with the perm specified as post-processing.
    6 Expand 1. "param_target": "inputs"
    values: Value of shape
    pre_process_transpose_perm: Transpose is applied to the tensor before the Expand operation with the perm specified as pre-processing.
    2. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the Expand operation with the perm specified as post-processing.
    7 Flatten 1. "param_target": "attributes"
    axis: Value of axis
    2. "param_target": "inputs"
    pre_process_transpose_perm: Transpose is applied to the tensor before the Flatten operation with the perm specified as pre-processing.
    3. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the Flatten operation with the perm specified as post-processing.
    8 Gemm
    9 Gather 1. "param_target": "attributes"
    axis: Value of axis
    2. "param_target": "inputs"
    values: Value of indices
    pre_process_transpose_perm: Transpose is applied to the tensor before the Gather operation with the perm specified as pre-processing.
    3. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the Gather operation with the perm specified as post-processing.
    10 MatMul 1. "param_target": "inputs"
    pre_process_transpose_perm: Transpose is applied to the tensor before the MatMul operation with the perm specified as pre-processing.
    2. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the MatMul operation with the perm specified as post-processing.
    11 Mul 1. "param_target": "inputs"
    values: Value of input
    pre_process_transpose_perm: Transpose is applied to the tensor before the Mul operation with the perm specified as pre-processing.
    2. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the Mul operation with the perm specified as post-processing.
    12 NonMaxSuppression
    13 ReduceL1
    ReduceL2
    ReduceLogSum
    ReduceLogSumExp
    ReduceMax
    ReduceMean
    ReduceMin
    ReduceProd
    ReduceSum
    ReduceSumSquare
    1. "param_target": "attributes"
    axes: Value of axes
    keepdims: Value of keepdims
    2. "param_target": "inputs"
    pre_process_transpose_perm: Transpose is applied to the tensor before the ReduceXX operation with the perm specified as pre-processing.
    3. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the ReduceXX operation with the perm specified as post-processing.
    14 Unsqueeze 1. "param_target": "inputs"
    pre_process_transpose_perm: Transpose is applied to the tensor before the Unsqueeze operation with the perm specified as pre-processing.
    2. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the Unsqueeze operation with the perm specified as post-processing.
    3. "param_target": "op"
    new_shape: Specifies directly the shape after Unsqueeze processing.
    {
      "op_name": "/backbone/backbone.1/Unsqueeze_1",
      "param_target": "op",
      "new_shape": [1,15,15,1]
    }
    15 Reshape 1. "param_target": "inputs"
    values: Value of shape
    pre_process_transpose_perm: Transpose is applied to the tensor before the Reshape operation with the perm specified as pre-processing.
    2. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the Reshape operation with the perm specified as post-processing.
    16 Resize 1. "param_target": "attributes"
    coordinate_transformation_mode: Value of coordinate_transformation_mode
    extrapolation_value: Value of extrapolation_value
    mode: Value of mode
    2. "param_target": "inputs"
    values: Value of roi or scales or sizes. scales=[scale_h,scale_w],sizes=[h,w]
    pre_process_transpose_perm: Transpose is applied to the tensor before the Resize operation with the perm specified as pre-processing.
    3. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the Resize operation with the perm specified as post-processing.
    17 Slice Slice implements special replacements separately ignore all automatic conversions and generate tf.strided_slice directly by specifying all parameters of tf.strided_slice directly.
    https://www.tensorflow.org/api_docs/python/tf/strided_slice
    See replace_slice.json for a sample description.
    20221221222956
    1. "param_target": "op"
    begin: Value of begin
    end: Value of end
    strides: Value of strides
    begin_mask: Value of begin_mask
    end_mask: Value of end_mask
    ellipsis_mask: Value of ellipsis_mask
    new_axis_mask: Value of new_axis_mask
    shrink_axis_mask: Value of shrink_axis_mask
    {
      "op_name": "/Slice",
      "param_target": "op",
      "begin": [0,0,1,0],
      "end": [0,0,0,0],
      "end_mask": 15
    }
    18 Softmax 1. "param_target": "attributes"
    axis: Value of axis. The transpositions corresponding to the specified axis are extrapolated before and after Softmax.
    2. "param_target": "inputs"
    values: Value of tensor
    19 Split 1. "param_target": "inputs"
    values: Value of split
    2. "param_target": "attributes"
    axis: Value of axis.
    num_outputs: Value of num_outputs.
    20 Sub 1. "param_target": "inputs"
    values: Value of input
    pre_process_transpose_perm: Transpose is applied to the tensor before the Sub operation with the perm specified as pre-processing.
    2. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the Sub operation with the perm specified as post-processing.
    21 Tile 1. "param_target": "inputs"
    values: Value of input
    pre_process_transpose_perm: Transpose is applied to the tensor before the Tile operation with the perm specified as pre-processing.
    2. "param_target": "outputs"
    post_process_transpose_perm: Transpose is applied to the tensor after the Tile operation with the perm specified as post-processing.
    22 Transpose 1. "param_target": "attributes"
    perm: Value of perm
    2. "param_target": "inputs"
    values: Value of tensor

Generated Model

Validated models (without replacement.json)

ONNX file for testing. https://github.com/PINTO0309/onnx2tf/releases/tag/1.1.28

See a list of verified models
No. Model Pass
1 age_googlenet.onnx ✔️
2 alike_t_opset11_192x320.onnx ✔️
3 arcfaceresnet100-8.onnx ✔️
4 baseline_simplified.onnx ✔️
5 big_slice_11.onnx ✔️
6 bvlcalexnet-12.onnx ✔️
7 caffenet-12.onnx ✔️
8 convtranspose_3_1_5_2.onnx ✔️
9 convtranspose_4_5_2_2.onnx ✔️
10 convtranspose_5_5_6_1.onnx ✔️
11 convtranspose_6_5_5_8.onnx ✔️
12 convtranspose_7_1_3_4.onnx ✔️
13 damoyolo_tinynasL20_T_192x192_post.onnx ✔️
14 deeplabv3_mobilenet_v3_large.onnx ✔️
15 densenet-12.onnx ✔️
16 depth_to_spase_17.onnx ✔️
17 double_gru.onnx ✔️
18 digits.onnx ✔️
19 detr_demo.onnx ✔️
20 efficientformer_l1.onnx ✔️
21 efficientdet_lite2_detection_1.onnx ✔️
22 efficientnet-lite4-11_nchw.onnx ✔️
23 effnet_opset11_dynamic_axis.onnx ✔️
24 emotion-ferplus-8_rename.onnx ✔️
25 face_detection_yunet_2022mar.onnx ✔️
26 face_recognition_sface_2021dec-act_int8-wt_int8-quantized.onnx ✔️
27 face_recognition_sface_2021dec.onnx ✔️
28 faster_rcnn-10.onnx ✔️
29 fastestdet.onnx ✔️
30 fused_conv_clip.onnx ✔️
31 fused_conv_hardsigmoid.onnx ✔️
32 fused_conv_leakyrelu.onnx ✔️
33 fused_conv_relu.onnx ✔️
34 fused_conv_sigmoid.onnx ✔️
35 fused_conv_tanh.onnx ✔️
36 gender_googlenet.onnx ✔️
37 gmflow-scale1-mixdata-train320x576-4c3a6e9a_1x3x480x640_bidir_flow_sim.onnx ✔️
38 handpose_estimation_mediapipe_2022may.onnx ✔️
39 htnet_1x17x2_without_norm.onnx ✔️
40 iat_llie_180x320.onnx ✔️
41 if_p1_11.onnx ✔️
42 if_p2_11.onnx ✔️
43 if_p3_11.onnx ✔️
44 imageclassifier.onnx ✔️
45 inception-v2-9.onnx ✔️
46 inverse11.onnx ✔️
47 mhformer_NxFxKxXY_1x27x17x2.onnx ✔️
48 mnist.onnx ✔️
49 mnist-12.onnx ✔️
50 mobilenetv2-12.onnx ✔️
51 mosaic_11.onnx ✔️
52 mosaic-9.onnx ✔️
53 movenet_multipose_lightning_192x256_p6.onnx ✔️
54 nanodet-plus-m_416.onnx ✔️
55 object_tracking_dasiamrpn_kernel_cls1_2021nov.onnx ✔️
56 object_tracking_dasiamrpn_kernel_r1_2021nov.onnx ✔️
57 object_tracking_dasiamrpn_model_2021nov.onnx ✔️
58 pidnet_S_cityscapes_192x320.onnx ✔️
59 ppmattingv2_stdc1_human_480x640.onnx ✔️
60 qlinear_conv_tensor_test.onnx ✔️
61 rcnn-ilsvrc13-9.onnx ✔️
62 regnet_x_400mf.onnx ✔️
63 ResNet101-DUC-12.onnx ✔️
64 resnet18-v1-7.onnx ✔️
65 resnet50-v1-12.onnx ✔️
66 resnet50-v2-7.onnx ✔️
67 retinanet-9.onnx ✔️
68 sinet_320_op.onnx ✔️
69 squeezenet1.0-12.onnx ✔️
70 super-resolution-10.onnx ✔️
71 swinir-m_64x64_12.onnx ✔️
72 text_recognition_CRNN_EN_2021sep.onnx ✔️
73 tinyyolov2-8.onnx ✔️
74 version-RFB-640.onnx ✔️
75 vit-b-32_textual.onnx ✔️
76 vit-b-32_visual.onnx ✔️
77 yolact_edge_mobilenetv2_550x550.onnx ✔️
78 yolact_regnetx_600mf_d2s_31classes_512x512.onnx ✔️
79 yolact_regnetx_800mf_20classes_512x512.onnx ✔️
80 yolo_free_nano_crowdhuman_192x320_post.onnx ✔️
81 yolov7_tiny_head_0.768_post_480x640.onnx ✔️
82 yolox_nano_192x192.onnx ✔️
83 yolox_nano_416x416.onnx ✔️
84 yolox_s.onnx ✔️
85 yolox_x_crowdhuman_mot17_bytetrack.onnx ✔️
86 zero_dce_640_dele.onnx ✔️
87 zfnet512-12.onnx ✔️

Key concept

List of Key concept
  • onnx-tensorflow is a very useful tool, but the performance of the generated TensorFlow models is significantly degraded due to the extrapolation of a large number of Transpose OPs before and after each OP during the format conversion from NCHW to NHWC. Therefore, I will make this tool myself as a derivative tool of onnx-tensorflow without extrapolating Transpose.

  • Most of the internal processing of the tool is full-scratch, but some of the more complex OPs have been adapted from onnx-tensorflow. I am very grateful to the engineers at International Business Machines Corporation / LeapMind / Microsoft / IBM for developing onnx-tensorflow.

  • I have incorporated all my knowledge of model optimization to other models such as TFLite, EdgeTPU, TensorFlow.js and Myriad based on my years of experience implementing openvino2tensorflow and tflite2tensorflow. It probably has the best model optimization performance and conversion efficiency of any tool I have created in the past, and the lowest rate of conversion errors.

  • Supported layers list. Supported layers

  • If you are having trouble with conversion errors, searching for resolved or open issues will almost always solve your problems. Issues are knowledge for engineers around the world.

  • Contributors to this repository should first read Contribution Guide.

    Kazam_screencast_00065_.mp4
  • All OPs are decomposed into primitive operations as much as possible. This is beneficial for lateral deployment of models to frameworks other than TFLite. Therefore, OPs belonging to tf.keras.layers are almost never used, and the tool consists only of tf.xxx. (except for a very few OPs)

  • As I do not want to add more dependent packages, I do not use tensorflow_addons (tfa), but replace it with the standard OP of tensorflow.

  • Not only does it handle conversions of 4-dimensional inputs, such as NCHW to NHWC, but also the number of input dimensions in 3, 5, or even more dimensions. For example, NCDHW to NDHWC, etc. However, since 1-D, 2-D, 3-D and 6-D input may produce patterns that are mechanically difficult to convert, it should be possible to give parameters to externally modify the tool's behavior. See Parameter replacement

  • If there are undefined dimensions in the input OP, the model structure is not fully optimized and conversion errors are very likely to occur.

  • Immediately following a Reshape OP with dimensional compression and dimensional decompression, there is a 95% probability that the model transformation operation will be disrupted and errors will occur. For example, patterns such as [1,200,200,5] -> [1,200,-1] or [10,20,30,40,50] -> [10,2,10,30,10,4,50] or Flatten. See #8 Not able to reshape input in replace.json, or #15 Conv layer shape wrong, or #18 Question about channel_transpose in common_functions.py, or #105 [MobileFormer]Converted model outputs values mismatch with original ones., or #133 When Onnx Matmul inputs have different dimension.

  • TensorFlow's Convolution does not have an equivalent operation to ONNX's Padding operation. Therefore, a Pad OP is inserted immediately before a Convolution with Padding of size greater than 1.

  • Support conversion to TensorFlow saved model and TFLite (Float32/Float16/INT8).

  • Files exceeding the Protocol Buffers file size limit of 2GB are not supported. Therefore, the external format is not supported at the initial stage of tool creation.

  • If there are ONNX OPs that are not supported by TensorFlow, use simple-onnx-processing-tools to replace them with harmless OPs in advance and then use this tool to convert them. In other words, you can convert any model with your efforts.

  • ONNX splitting, merging, generating OPs, rewriting OP attributes, BGR<->RGB conversion, converting to JSON and editing in the IDE, batch size changes for undefined dimensions, and various other processing can be done with the simple-onnx-processing-tools. Therefore, it is recommended that models with very complex structures be converted to TFLite after modifying the structure beforehand.

  • BatchNormalization supports only inference mode.

  • LayerNormalization supports only inference mode.

  • Only for opset=11 or higher

  • If you do not like the generated TFLite OP name, edit it using tflite2json2tflite.

  • The generated Keras models cannot be used for retraining. If you want to train, you must build your own model.

  • When converting to TensorFlow.js, CoreML, etc., please generate saved_model with the --output_signaturedefs option and use the generated saved_model to convert with various converters. tensorflowjs_converter, coremltools, edgetpu_compilier, etc... If this option is not enabled, saved_model records only the minimum necessary information and its size is minimized. When this option is enabled, saved_model records the maximum amount of information, and instead of being maximized in size, the output is in a format that supports conversion to other frameworks. It can also be used for serving.

  • There are many OPs on ONNX that do not support TFLite/EdgeTPU/TFJS/CoreML/TensorRT. Therefore, if you need to generate an EdgeTPU model, please specify --replace_to_pseudo_operators to convert your model. onnx2tf will attempt to replace the OP with an TFLite/EdgeTPU/TFJS/CoreML/TensorRT-compatible OP whenever possible.

  • The main factors that cause accuracy degradation after model conversion are as follows

  1. differences in Padding specifications
  2. difference in Python division specification in the process of model transformation (error due to even rounding)
  3. Divide epsilon without consideration
  4. deprecated TrueDivision
  5. support difference of powers
  6. differences in interpolation operation specifications during resizing
  7. Difference in arithmetic precision supported by each operation
  8. Calculation error due to scaling up or down by specifying a scale when resizing images

The above differences often cannot be dealt with by simply converting the model in a straightforward manner. Therefore, you need to replace the model yourself in advance with an operation that is less prone to errors.

  • Support for INT8 Quantization, Full INT8 Quantization, INT8 Quantization with INT16 activation, Full INT8 Quantization with INT16 activation and Dynamic Range Quantization.
  • Support for Per-Channel Quantization and Per-Tensor Quantization.
  • Support for GroupConvolution.
  • TFLite does not support TrueDiv(INT), so TrueDiv is avoided if possible.
  • Implement the Resize process for the 5D tensor.
  • Add process to replace Asin with pseudo-Asin.
  • Add process to replace Acos with pseudo-Acos.
  • Add process to replace Atan with pseudo-Atan.
  • Add process to replace Abs with pseudo-Abs.
  • Add process to replace GatherND with pseudo-GatherND.
  • Add process to replace HardSwish with pseudo-HardSwish.
  • Add process to replace GridSample with pseudo-GridSample.
  • Add process to replace PRelu with pseudo-PRelu.
  • Add process to replace LeakyRelu with pseudo-LeakyRelu.
  • Add process to replace Power with pseudo-Power.
  • Add process to replace Neg with pseudo-Neg.
  • Add process to replace ArgMax with pseudo-ArgMax.
  • Add process to replace Erf with pseudo-Erf.
  • Add process to replace GeLU with pseudo-GeLU.
  • Added option to fix dynamic batch size N to a specified number.
  • Added option to overwrite dynamic shape input OPs with static shape. --overwrite_input_shape
  • Output in Keras H5 format.
  • Automatically run onnx-simplifier (onnxsim) backend and optimize onnx files before model transformation.
  • Added the ability to automatically generate each OP name and assign OP names to ONNX files in the old format.
  • Supports model splitting. Interrupts model transformation at the specified output name and outputs the model partitioned into subgraphs.

Related tools

  1. tflite2tensorflow
  2. openvino2tensorflow
  3. tflite2json2tflite
  4. tensorflowjs_converter
  5. coremltools
  6. simple-onnx-processing-tools
  7. tflite-input-output-rewriter
  8. onnx-simplifier
  9. onnx_graphsurgeon
  10. onnx
  11. onnx-tensorflow
  12. onnx2keras
  13. TinyNeuralNetwork
  14. nobuco
  15. onnx2torch

Acknowledgement

  1. https://github.com/onnx/models
  2. https://github.com/opencv/opencv_zoo
  3. https://pytorch.org/vision/stable/models.html
  4. https://tfhub.dev/
  5. https://www.kaggle.com/models
  6. https://github.com/TexasInstruments/edgeai-modelzoo

Contributors

Made with contrib.rocks.

onnx2tf's People

Contributors

babiking avatar bas-aarts avatar boulaouaney avatar democat3457 avatar dimalukas avatar fateshelled avatar guanerdan avatar hyunseok-kim0 avatar jpowie01 avatar kozistr avatar mikel-brostrom avatar motokimura avatar nguyencse avatar on-jungwoan avatar pinto0309 avatar radekzc avatar shakarim94 avatar spacycoder avatar svobora avatar ysohma avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

onnx2tf's Issues

Yolov7-tiny to TensorflowLite conversion results in a dynamic output model incompatible with TfLite Java API

Issue Type

Others

onnx2tf version number

1.5.36

onnx version number

1.12.0

tensorflow version number

2.10.1

Download URL for ONNX

pip install onnx==1.12.0

Parameter Replacement JSON

none

Description

Hi, your library is awesome!

I converted the Yolov7-tiny from PyTorch to TfLite using:
onnx2tf -i yolov7-tiny.onnx -o models-NHWC-final/ -osd -oh5 -cotof

I am trying to use it on an android device. The model works when tested on a PC however the Tensorflow Java API for Android does not support dynamic output models according to their documentation: https://www.tensorflow.org/lite/guide/inference. While the resulting yolo tflite model has a dynamic number of outputs( the number of outputs change with the number of indications/detentions)

On the other hand, if I follow the conversion path PyTorch -> ONNX-> Tensorflow I do get yolov7 with a fixed output size so I suspect it is possible to achieve this with onnx2tf as well while also doing the NcHW to NHWc conversion in the process.

Is there a way to have onnx2tf output a fixed/static output .tflite model for yolov7-tiny?

Thank you

[human_segmentation_pphumanseg_2021oct] Failed to run test sample. The inequality of unknown TensorShapes is undefined

Issue Type

Others

onnx2tf version number

1.2.13

Download URL for ONNX

wget https://github.com/PINTO0309/onnx2tf/releases/download/1.1.27/human_segmentation_pphumanseg_2021oct.onnx

Parameter Replacement JSON

{
  "format_version": 1,
  "operations": [
    {
      "op_name": "StatefulPartitionedCall/Tile_4",
      "param_target": "inputs", # attributes or inputs
      "param_name": "const_fold_opt__677",
      "values": [1,1,17] # Disable parameter transposition or overwrite parameters
    },
    {
      "op_name": "StatefulPartitionedCall/Cast_3",
      "param_target": "attributes", # attributes or inputs
      "param_name": "to",
      "values": 1 # Disable parameter transposition or overwrite "to" parameters
    },
    {
      "op_name": "Resize__697",
      "param_target": "inputs",
      "param_name": "Concat__696:0",
      "values": [26,26] # Replacement of unk__x (Resize OP, sizes height/width parameter)
    },
    {
      "op_name": "Transpose__927",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3] # Disable parameter transposition or overwrite "perm" parameters
    },
    {
      "op_name": "StatefulPartitionedCall/functional_1/max_unpooling2d_2/Reshape_1",
      "param_target": "inputs",
      "param_name": "const_fold_opt__911",
      "values": [4,131072] # Overwrite "shape" parameters
    },
    {
      "op_name": "Reshape_25",
      "param_target": "outputs",
      "param_name": "onnx::InstanceNormalization_270",
      "post_process_transpose_perm": [0,2,1] # Extrapolate 3D Transpose after Reshape
    },
    {
      "op_name": "Reshape_30",
      "param_target": "outputs",
      "param_name": "onnx::Mul_275",
      "post_process_transpose_perm": [0,2,3,1] # Extrapolate 4D Transpose after Reshape
    },
    {
      "op_name": "flatten_1127",
      "param_target": "inputs",
      "param_name": "dropout0",
      "pre_process_transpose_perm": [0,3,1,2]
    }
  ]
}

Description

Failed to run test sample in CLI. Use this:

wget https://github.com/PINTO0309/onnx2tf/releases/download/1.1.27/human_segmentation_pphumanseg_2021oct.onnx
wget https://github.com/PINTO0309/onnx2tf/releases/download/1.1.27/replace.json
onnx2tf -i human_segmentation_pphumanseg_2021oct.onnx -prf replace.json

Encountered:

Model optimizing started ============================================================
WARNING: Failed to optimize the onnx file.
Traceback (most recent call last):
  File "C:\Users\zhugc\AppData\Roaming\Python\Python39\site-packages\onnx2tf\onnx2tf.py", line 427, in convert
    result = subprocess.check_output(
  File "D:\Anaconda3\lib\subprocess.py", line 424, in check_output
    return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
  File "D:\Anaconda3\lib\subprocess.py", line 528, in run
    raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['onnxsim', 'human_segmentation_pphumanseg_2021oct.onnx', 'human_segmentation_pphumanseg_2021oct.onnx']' returned non-zero exit status 1.

Automatic generation of each OP name started ========================================
Automatic generation of each OP name complete!

Model loaded ========================================================================

Model convertion started ============================================================
INFO: input_op_name: x shape: [1, 3, 192, 192] dtype: float32
ERROR: The trace log is below.
Traceback (most recent call last):
  File "C:\Users\zhugc\AppData\Roaming\Python\Python39\site-packages\onnx2tf\utils\common_functions.py", line 262, in print_wrapper_func
    result = func(*args, **kwargs)
  File "C:\Users\zhugc\AppData\Roaming\Python\Python39\site-packages\onnx2tf\ops\Input.py", line 81, in make_node
    if graph_input.shape != tf.TensorShape(None) and len(graph_input.shape) in [3, 4, 5] \
  File "C:\Users\zhugc\AppData\Roaming\Python\Python39\site-packages\tensorflow\python\framework\tensor_shape.py", line 1294, in __ne__
    raise ValueError("The inequality of unknown TensorShapes is undefined.")
ValueError: The inequality of unknown TensorShapes is undefined.
ERROR: Read this and deal with it. https://github.com/PINTO0309/onnx2tf#parameter-replacement
ERROR: Alternatively, if the input OP has a dynamic dimension, use the -b or -ois option to rewrite it to a static shape and try again.
ERROR: If the input OP of ONNX before conversion is NHWC, use the -kt option.

Not able to reshape input in replace.json

Issue Type

Others

onnx2tf version number

1.0.27

Download URL for ONNX

https://drive.google.com/file/d/1Wkb-xi8NBICJttIyctUL3m7ETGHv2BZ8/view

Parameter Replacement JSON

{
    "format_version": 1,
    "operations": [
        {
            "op_name": "Flatten_10",
            "param_target": "inputs",
            "param_name": "input.16",
            "pre_process_transpose_perm": [3,1,2,0]
        },
        {
            "op_name": "Flatten_10",
            "param_target": "outputs",
            "param_name": "onnx::Gemm_31",
            "post_process_transpose_perm": [1, 0]
        }
    ]
}

Description

  1. School research project on TinyML.
  2. I have simplified the model with onnxsim, then I ran this command onnx2tf -i baseline_simplified.onnx -b 1

The onnx model has a Flatten layer. Input shape is ['batch_size', 32, 1, 3], output shape is ['batch_size', 96]. The OP get converted to tf.reshape, the input shape is (1, 1, 3, 32), output shape is (3, 32).
image

  1. I attempted to solve it with the Parameter Replacement JSON to tranpose both the input and output. The transpose for the output works, but the transpose for the input gives me this error.
    image

  2. I need the tflite model so that I can quantise it and deploy using TFLiteMicro.

  3. I have tried to use openvino library to convert onnx to openvino, then openvino to tf. I can convert but the output of tf model is very different from the onnx output.

Implementation of strict mode

Issue Type

Others

onnx2tf version number

1.7.x

onnx version number

1.13.0

tensorflow version number

2.10.0

Download URL for ONNX

N/A

Parameter Replacement JSON

N/A

Description

  1. Personal
  2. Add an ultra-slow conversion mode to strictly avoid shape unmatch errors and precision errors.

Background

Since the internal processing has already implemented a number of fairly complex workarounds to avoid conversion errors, we have not dared to implement a correction process for accuracy errors at this time in order not to further increase the complexity of the logic. Instead, I have implemented a mechanism whereby the user visually identifies which operation's channel transpositions cause problems, and the user changes the behavior of the tool himself.

However, I cannot overlook the situation where accuracy errors remain despite successful model transformations, and I hope to eradicate them in the future. The work to change the behavior of the tool by the users themselves with the users' visibility of the problem areas is very costly.

Since the internal processing mechanism needs to be significantly revised, we intend to implement many additional test modifications gradually in minor version upgrades to the extent that they do not affect the existing processing.

Idea

  • Add all OPs of ONNX to the output OP of the graph.
  • Run ONNX inference with dummy tensor only once to get the sample inference results for all OPs.
  • Always compare the contents of the ONNX output tensor and TensorFlow output tensor before converting each OP.
  • Channel placement will always be inconsistent between ONNX and TensorFlow, so the following implementation can be called from all OPs at any time.
    def onnx_tf_tensor_validation(
    *,
    onnx_tensor_infos: Dict[str, np.ndarray],
    tf_tensor_infos: Dict[str, np.ndarray],
    rtol: float=1e-05,
    atol: float=1e-05,
    ) -> Dict[str, List]:
    """Check if the ONNX tensor and the TF tensor are approximate.
    Parameters
    ----------
    onnx_tensor_infos: Dict[str, np.ndarray]
    ONNX tensor to be verified
    {
    output_name: np.ndarray,
    output_name: np.ndarray,
    :
    }
    tf_tensors: List[np.ndarray]
    TF tensor to be verified
    [
    np.ndarray,
    np.ndarray,
    :
    ]
    rtol: float=1e-05
    The relative tolerance parameter
    atol: float=1e-05
    The absolute tolerance parameter
    Returns
    ----------
    check_results: Dict[str, List[np.ndarray, int]]
    Tensor Comparison Results
    {
    onnx_output_name: [
    onnx_tensor,
    matched_flg, <--- 0: Unmatched, 1: Matched, 2: Skipped (Deleted or Shape Unmatched),
    max_abs_err,
    ]
    }
    """
    check_results = {
    onnx_output_name: [onnx_tensor, False, 0.0] \
    for onnx_output_name, onnx_tensor in onnx_tensor_infos.items()
    }
    for onnx_output_name, onnx_check_info in check_results.items():
    onnx_tensor: np.ndarray = onnx_check_info[0] # onnx_tensor
    tf_tensor: np.ndarray = tf_tensor_infos[onnx_output_name] # tf_tensor
    onnx_tensor_shape = onnx_tensor.shape
    max_abs_err = ONNX_INF_INDEX_VALUE
    """
    onnx_dummy_data: np.random.random_sample([1,3,224,224])
    tf_dummy_data : onnx_dummy_data.transpose([0,2,3,1]), len(tf_tensor.shape) == 4
    tf_shape_transpose_perms:
    [
    (0, 1, 2, 3), (0, 1, 3, 2), (0, 2, 1, 3), (0, 2, 3, 1), (0, 3, 1, 2),
    (0, 3, 2, 1), (1, 0, 2, 3), (1, 0, 3, 2), (1, 2, 0, 3), (1, 2, 3, 0),
    (1, 3, 0, 2), (1, 3, 2, 0), (2, 0, 1, 3), (2, 0, 3, 1), (2, 1, 0, 3),
    (2, 1, 3, 0), (2, 3, 0, 1), (2, 3, 1, 0), (3, 0, 1, 2), (3, 0, 2, 1),
    (3, 1, 0, 2), (3, 1, 2, 0), (3, 2, 0, 1), (3, 2, 1, 0)
    ]
    tf_target_transpose_perms:
    [(0, 3, 1, 2), (0, 3, 2, 1)]
    """
    tf_shape_transpose_perms = list(itertools.permutations(range(len(tf_tensor.shape))))
    tf_target_transpose_perms = [
    tf_shape_transpose_perm \
    for tf_shape_transpose_perm in tf_shape_transpose_perms \
    if tf_tensor.transpose(tf_shape_transpose_perm).shape == onnx_tensor_shape
    ]
    # Validation
    """
    tf_check_infos:
    {
    [
    tf_target_transpose_perm, <--- tf_target_transpose_perms[idx]
    matched_flg, <--- True: Matched, False: Unmatched
    ]
    }
    """
    validate_result = False
    tf_check_infos = [
    [tf_target_transpose_perm, 0] for tf_target_transpose_perm in tf_target_transpose_perms
    ]
    for tf_check_info in tf_check_infos:
    if len(onnx_tensor_shape) > 1:
    tf_transposed_tensor = tf_tensor.transpose(tf_check_info[0])
    if np.allclose(a=onnx_tensor, b=tf_transposed_tensor, rtol=rtol, atol=atol, equal_nan=True):
    # Matched
    tf_check_info[1] = 1
    max_abs_err = 0.0
    break
    else:
    # Unmatched
    if onnx_tensor.shape == tf_transposed_tensor.shape:
    error_value = np.max(np.abs(onnx_tensor - tf_transposed_tensor))
    max_abs_err = error_value if error_value < max_abs_err else max_abs_err
    else:
    tf_check_info[1] = 2
    max_abs_err = 0.0
    # Validation results check
    for tf_check_info in tf_check_infos:
    if tf_check_info[1]:
    validate_result = tf_check_info[1]
    break
    if not validate_result and max_abs_err == ONNX_INF_INDEX_VALUE:
    # Tensors deleted from the TensorFlow model structure during
    # the model optimization process are not comparable,
    # so the status is rewritten to Skip.
    # If there was no match between ONNX and TensorFlow output shapes.
    check_results[onnx_output_name][1] = 2
    check_results[onnx_output_name][2] = max_abs_err
    else:
    check_results[onnx_output_name][1] = validate_result
    check_results[onnx_output_name][2] = max_abs_err
    return check_results
  • Search for the channel arrangement with the lowest accuracy error.
  • The Transpose OP is excluded from the search because the value to be compared does not change.
  • Not implemented for Constant and ConstantOfShape.
  • Other than the above, OPs that do not involve conversion of values are excluded from processing.
  • For error correction, the same method as for pre_process_transpose_perm is used to check all combinations that pre-transpose the input tensor to the corresponding OP.
  • If the target OP is specified in parameter_replacement.json, omit processing.
  • Remove any intrinsic processing already implemented in MatMul.
  • OPs with both attribute and tensor values, such as Softmax, attempt correction in the following order: attribute value change first, then tensor transposition.

[vae_encoder] Error when converting vae_encoder.onnx

Issue Type

Others

onnx2tf version number

1.1.15

Download URL for ONNX

https://github.com/PINTO0309/onnx2tf/releases/download/1.1.15/vae_encoder.onnx

Parameter Replacement JSON

{
  "format_version": 1,
  "operations": [
    {
      "op_name": "Reshape_448",
      "param_target": "outputs",
      "param_name": "onnx::Add_817",
      "post_process_transpose_perm": [0,2,3,1]
    }
  ]
}

Description

  1. Error when converting vae_encoder.onnx. For StableDiffusion [Hugging Face].
  • Error message
    onnx2tf -i vae_encoder.onnx
    ERROR: The trace log is below.
    Traceback (most recent call last):
      File "/home/xxxxx/git/onnx2tf/onnx2tf/utils/common_functions.py", line 261, in print_wrapper_func
        result = func(*args, **kwargs)
      File "/home/xxxxx/git/onnx2tf/onnx2tf/utils/common_functions.py", line 323, in inverted_operation_enable_disable_wrapper_func
        result = func(*args, **kwargs)
      File "/home/xxxxx/git/onnx2tf/onnx2tf/ops/Add.py", line 79, in make_node
        tf.math.add(
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/util/traceback_utils.py", line 153, in error_handler
        raise e.with_traceback(filtered_tb) from None
      File "/usr/local/lib/python3.8/dist-packages/keras/layers/core/tf_op_layer.py", line 119, in handle
        return TFOpLambda(op)(*args, **kwargs)
      File "/usr/local/lib/python3.8/dist-packages/keras/utils/traceback_utils.py", line 70, in error_handler
        raise e.with_traceback(filtered_tb) from None
    ValueError: Exception encountered when calling layer "tf.math.add_56" (type TFOpLambda).
    
    Dimensions must be equal, but are 512 and 64 for '{{node tf.math.add_56/Add}} = AddV2[T=DT_FLOAT](Placeholder, Placeholder_1)' with input shapes: [1,512,64,64], [1,64,64,512].
    
    Call arguments received by layer "tf.math.add_56" (type TFOpLambda):
      • x=tf.Tensor(shape=(1, 512, 64, 64), dtype=float32)
      • y=tf.Tensor(shape=(1, 64, 64, 512), dtype=float32)
      • name='Add_449'
    ERROR: Read this and deal with it. https://github.com/PINTO0309/onnx2tf#parameter-replacement
    ERROR: Alternatively, if the input OP has a dynamic dimension, use the -b or -ois option to rewrite it to a static shape and try again.
    ERROR: If the input OP of ONNX before conversion is NHWC, use the -kt option.
    
  1. To avoid conversion errors, write replace.json and run it.
    onnx2tf -i vae_encoder.onnx -prf replace.json

The final conversion was successful. https://github.com/PINTO0309/onnx2tf/releases/tag/1.1.15

[MobileBERT] RecursionError: maximum recursion depth exceeded

Issue Type

Others

onnx2tf version number

1.5.16

onnx version number

1.13.0

tensorflow version number

2.10.0

Download URL for ONNX

  • tflite
    https://tfhub.dev/tensorflow/lite-model/mobilebert/1/metadata/1?lite-format=tflite

  • tflite to onnx

    python -m tf2onnx.convert \
    --opset 11 \
    --tflite lite-model_mobilebert_1_metadata_1.tflite \
    --output lite_model_mobilebert_1_metadata_1.onnx
    
    onnxsim lite_model_mobilebert_1_metadata_1.onnx lite_model_mobilebert_1_metadata_1.onnx
    onnxsim lite_model_mobilebert_1_metadata_1.onnx lite_model_mobilebert_1_metadata_1.onnx
    onnxsim lite_model_mobilebert_1_metadata_1.onnx lite_model_mobilebert_1_metadata_1.onnx
    onnxsim lite_model_mobilebert_1_metadata_1.onnx lite_model_mobilebert_1_metadata_1.onnx
    onnxsim lite_model_mobilebert_1_metadata_1.onnx lite_model_mobilebert_1_metadata_1.onnx
  • onnx to tflite

    onnx2tf -i lite_model_mobilebert_1_metadata_1.onnx -cotof -cotoa 1e-1 -cgdc
    

Parameter Replacement JSON

N/A

Description

The model structure was too complex and overflowed python's Maximum Recursion Depth.
The following must be added to the code to avoid this.

sys.setrecursionlimit(10000)
  1. Personal
  2. Error
    Traceback (most recent call last):
      File "/home/b920405/.local/bin/onnx2tf", line 33, in <module>
        sys.exit(load_entry_point('onnx2tf', 'console_scripts', 'onnx2tf')())
      File "/usr/local/lib/python3.8/dist-packages/onnx2tf/onnx2tf.py", line 1698, in main
        model = convert(
      File "/usr/local/lib/python3.8/dist-packages/onnx2tf/onnx2tf.py", line 718, in convert
        model = tf.keras.Model(inputs=inputs, outputs=outputs)
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/trackable/base.py", line 205, in _method_wrapper
        result = method(self, *args, **kwargs)
      File "/usr/local/lib/python3.8/dist-packages/keras/engine/functional.py", line 165, in __init__
        self._init_graph_network(inputs, outputs)
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/trackable/base.py", line 205, in _method_wrapper
        result = method(self, *args, **kwargs)
      File "/usr/local/lib/python3.8/dist-packages/keras/engine/functional.py", line 264, in _init_graph_network
        nodes, nodes_by_depth, layers, _ = _map_graph_network(
      File "/usr/local/lib/python3.8/dist-packages/keras/engine/functional.py", line 1046, in _map_graph_network
        nodes_in_decreasing_depth, layer_indices = _build_map(outputs)
      File "/usr/local/lib/python3.8/dist-packages/keras/engine/functional.py", line 1176, in _build_map
        _build_map_helper(
      File "/usr/local/lib/python3.8/dist-packages/keras/engine/functional.py", line 1219, in _build_map_helper
        _build_map_helper(
      File "/usr/local/lib/python3.8/dist-packages/keras/engine/functional.py", line 1219, in _build_map_helper
        _build_map_helper(
      File "/usr/local/lib/python3.8/dist-packages/keras/engine/functional.py", line 1219, in _build_map_helper
        _build_map_helper(
      [Previous line repeated 986 more times]
      File "/usr/local/lib/python3.8/dist-packages/keras/engine/functional.py", line 1199, in _build_map_helper
        node = layer._inbound_nodes[node_index]
    RecursionError: maximum recursion depth exceeded
    

Bias from Conv2d operator seems to be added wrongly.

Issue Type

Others

onnx2tf version number

0.28

Download URL for ONNX

Added screenshots of the models to show bias values

Description

My model. is getting converted properly but I see results mismatch, so. trying to. figure out whats causing differences.
One of the possible issues is due to how. the bias added from Conv2D operator while conversion.
Adding screenshots of the bias in Conv2D. operator. from original. onnx model and corresponding conversion to tf. using the tool.
You can see the bias at index [1] is added in the last. Looks weird. Please correct me If I am wrong.

onnn-mod. l

tf-converted

[FastestDet] ValueError: Exception encountered when calling layer "tf.nn.convolution_4" (type TFOpLambda)

Issue Type

Others

onnx2tf version number

1.1.38

Download URL for ONNX

Parameter Replacement JSON

  • replace.json
    {
      "format_version": 1,
      "operations": [
        {
          "op_name": "Gather_18",
          "param_target": "outputs",
          "param_name": "onnx::Concat_445",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Gather_33",
          "param_target": "outputs",
          "param_name": "onnx::Concat_463",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Gather_48",
          "param_target": "outputs",
          "param_name": "onnx::Concat_481",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Gather_72",
          "param_target": "outputs",
          "param_name": "onnx::Concat_513",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Gather_87",
          "param_target": "outputs",
          "param_name": "onnx::Concat_531",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Gather_102",
          "param_target": "outputs",
          "param_name": "onnx::Concat_549",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Gather_117",
          "param_target": "outputs",
          "param_name": "onnx::Concat_567",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Gather_132",
          "param_target": "outputs",
          "param_name": "onnx::Concat_585",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Gather_147",
          "param_target": "outputs",
          "param_name": "onnx::Concat_603",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Gather_162",
          "param_target": "outputs",
          "param_name": "onnx::Concat_621",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Gather_186",
          "param_target": "outputs",
          "param_name": "onnx::Concat_653",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Gather_201",
          "param_target": "outputs",
          "param_name": "onnx::Concat_671",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Gather_216",
          "param_target": "outputs",
          "param_name": "onnx::Concat_689",
          "post_process_transpose_perm": [0,2,3,1]
        },
        {
          "op_name": "Transpose_261",
          "param_target": "attributes",
          "param_name": "perm",
          "values": [0,1,2,3]
        },
        {
          "op_name": "Transpose_263",
          "param_target": "attributes",
          "param_name": "perm",
          "values": [0,1,2,3]
        }
      ]
    }

Description

  1. Research
  2. Conversion error occured.
onnx2tf -i FastestDet.onnx
ERROR: The trace log is below.
Traceback (most recent call last):
  File "/home/b920405/git/onnx2tf/onnx2tf/utils/common_functions.py", line 262, in print_wrapper_func
    result = func(*args, **kwargs)
  File "/home/b920405/git/onnx2tf/onnx2tf/utils/common_functions.py", line 324, in inverted_operation_enable_disable_wrapper_func
    result = func(*args, **kwargs)
  File "/home/b920405/git/onnx2tf/onnx2tf/ops/Conv.py", line 153, in make_node
    tf.nn.convolution(
  File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/util/traceback_utils.py", line 153, in error_handler
    raise e.with_traceback(filtered_tb) from None
  File "/usr/local/lib/python3.8/dist-packages/keras/layers/core/tf_op_layer.py", line 119, in handle
    return TFOpLambda(op)(*args, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/keras/utils/traceback_utils.py", line 70, in error_handler
    raise e.with_traceback(filtered_tb) from None
ValueError: Exception encountered when calling layer "tf.nn.convolution_4" (type TFOpLambda).

Depth of input (44) is not a multiple of input depth of filter (24) for '{{node tf.nn.convolution_4/convolution}} = Conv2D[T=DT_FLOAT, data_format="NHWC", dilations=[1, 1, 1, 1], explicit_paddings=[], padding="SAME", strides=[1, 1, 1, 1], use_cudnn_on_gpu=true](Placeholder, tf.nn.convolution_4/convolution_internal/filters)' with input shapes: [1,24,44,44], [1,1,24,24].
  1. Parameter Replacement JSON
  2. Research

[MobileFormer]Dimensions must be equal [Add Layer]

Issue Type

Others

onnx2tf version number

1.4.2

onnx version number

1.12.0

tensorflow version number

2.10.1

Download URL for ONNX

https://drive.google.com/file/d/1vGzO9MZGX-yGz6ATm4yHVJMASZACuy2t/view?usp=share_link

Parameter Replacement JSON

{
  "format_version": 1,
  "operations": [
    {
      "op_name": "Add_282",
      "param_target": "pre_process_transpose_perm", 
      "param_name": "perm",
      "values": [0, 3, 1, 2]
    }
  ]
}

Description

Hi, thank you so much for actively maintaining this useful repo. I am trying to convert my custom pytorch model to onnx and then to keras for further tuning. I exported the model using opset==12. The issue occurred during convertion with an ADD LAYER when I run convert('onnx/py_model.onnx').

In attempting to solve this problem, I provided the parameter replacement file to the converter but got the same issue. It looks like one input for the layer is in NHWC and the other is in NCHW for op_name Add_282 (number 1 on the screenshot) in this case. From the error, I can see that one of the input for add is in correct order(NCHW) but the other one is in NHWC. So I look back to the operation Add_263 (number 2 on the screenshot) that is responsible for generating the wrong input and I found that the keras input for this operation is already in NHWC. I did not trace further up because I think that illustrate my problem: it seems like one of the input for the Add_282 is in correct shape.

So I wonder if there is some quick fix for this, like if I forgot to set some parameter or did incorrectly for parameter replacement, or version issue? I also find #17 but at the very end you said no need to do parameter replacement for transpose operation after 1.1.38 and after opset>11. I apologize in advance if I missed something super obvious, but could you provide some tips on how to fix this? Thanks again!

Screenshot

screenshot

Traceback (most recent call last):
  File "C:\Users\qz796\anaconda3\envs\phoodify\lib\site-packages\onnx2tf\utils\common_functions.py", line 267, in print_wrapper_func
    result = func(*args, **kwargs)
  File "C:\Users\qz796\anaconda3\envs\phoodify\lib\site-packages\onnx2tf\utils\common_functions.py", line 329, in inverted_operation_enable_disable_wrapper_func
    result = func(*args, **kwargs)
  File "C:\Users\qz796\anaconda3\envs\phoodify\lib\site-packages\onnx2tf\utils\common_functions.py", line 37, in get_replacement_parameter_wrapper_func
    func(*args, **kwargs)
  File "C:\Users\qz796\anaconda3\envs\phoodify\lib\site-packages\onnx2tf\ops\Add.py", line 126, in make_node
    tf.math.add(
  File "C:\Users\qz796\anaconda3\envs\phoodify\lib\site-packages\tensorflow\python\util\traceback_utils.py", line 153, in error_handler
    raise e.with_traceback(filtered_tb) from None
  File "C:\Users\qz796\anaconda3\envs\phoodify\lib\site-packages\keras\layers\core\tf_op_layer.py", line 119, in handle
    return TFOpLambda(op)(*args, **kwargs)
  File "C:\Users\qz796\anaconda3\envs\phoodify\lib\site-packages\keras\utils\traceback_utils.py", line 70, in error_handler
    raise e.with_traceback(filtered_tb) from None
ValueError: Exception encountered when calling layer "tf.math.add_101" (type TFOpLambda).

Dimensions must be equal, but are 56 and 16 for '{{node tf.math.add_101/Add}} = AddV2[T=DT_FLOAT](Placeholder, Placeholder_1)' with input shapes: [1,56,56,16], [1,16,56,56].

Call arguments received by layer "tf.math.add_101" (type TFOpLambda):
  • x=tf.Tensor(shape=(1, 56, 56, 16), dtype=float32)
  • y=tf.Tensor(shape=(1, 16, 56, 56), dtype=float32)
  • name='Add_248'

protobuf version issue when trying to install packages in colab

Issue Type

Others

onnx2tf version number

N/A

onnx version number

N/A

tensorflow version number

2.10.0

Download URL for ONNX

N/A

Parameter Replacement JSON

N/A

Description

I tried to install onnx2tf in colab as instructed

!sudo add-apt-repository -y ppa:deadsnakes/ppa
...
!python3.9 -m pip install tensorflow==2.10.0 \
...
  && python3.9 -m pip install -U onnx2tf \
  && python3.9 -m pip install -U protobuf==3.20.3

But I got the following error

tensorflow 2.10.0 requires protobuf<3.20,>=3.9.2, but you have protobuf 3.20.3 which is incompatible.
tensorboard 2.10.1 requires protobuf<3.20,>=3.9.2, but you have protobuf 3.20.3 which is incompatible.

Is that expected?

Thanks!

[TODO] Approximate computation implementation of `Atan` / `Atan2`

Issue Type

Others

onnx2tf version number

1.5.x

onnx version number

1.13.1

tensorflow version number

2.10.0

Download URL for ONNX

N/A

Parameter Replacement JSON

N/A

Description

  1. Personal
  2. Approximate computation implementation of Atan / Atan2
  3. Refs:
    1. https://developer.download.nvidia.com/cg/atan.html
    2. https://developer.download.nvidia.com/cg/atan2.html
    3. https://zenn.dev/pinto0309/articles/8f6df1d2304395

[silero_vad] Question about Pad

Issue Type

Others

onnx2tf version number

1.2.4

Download URL for ONNX

https://github.com/snakers4/silero-vad/blob/v3.1/files/silero_vad.onnx

Parameter Replacement JSON

{}

Description

  1. Purpose: Personal development
  2. What: Convert error occured
    Running script: onnx2tf -i silero_vad.onnx

INFO: onnx_op_type: Pad
INFO: input shape: [1, 1, 1, 512]
...
INFO: output shape: [1, 1, 1, 768]
INFO: tf_op_type: Pad
INFO: input shape: [1, 1, 1, 512]
...
INFO: output shape: [1, 1, 257, 512]

ValueError: Exception encountered when calling layer "tf.squeeze"
Can not squeeze dim[3], expected a dimension of 1, got 512

So why the Pad's behavior is not the same during conversion and what should I do?

  1. How: I've tried Parameter replacement but the error still occured. So I think it is related to the dimensional decompression after Reshape.
  2. Why: Because I want tflite.

[Yolov7] Yolov7 problem with detection coordinates

Issue Type

Others

onnx2tf version number

1.1.38

Download URL for ONNX

https://github.com/triptec/detection-models/blob/master/yolov7/yolov7-tiny.onnx

Parameter Replacement JSON

{}

Description

  1. Personal project
  2. I'm trying to convert a yolov7-tiny.onnx to a tflite int8 model.
    First I export to onnx with:
    python export.py --weights ../models/yolov7/yolov7-tiny.pt --grid --simplify
    Then convert it with:
    onnx2tf -i yolov7-tiny.onnx -ioqd uint8 -oiqt
    Finaly I run (using https://github.com/ultralytics/yolov5/blob/master/detect.py)
    python detect.py --weights ../models/yolov7/yolov7-tiny.tflite --conf 0.25 --img-size 640 --source data/images/zidane.jpg

And it results in this:

tensor([2.39083e+05, 1.02942e+05, 3.69536e+05, 3.17344e+05, 7.92245e-01, 0.00000e+00])
tensor([1.40084e+05, 2.27828e+05, 1.72367e+05, 3.18328e+05, 5.62911e-01, 2.70000e+01])
tensor([4.15838e+04, 1.53934e+05, 3.06103e+05, 3.16576e+05, 5.42062e-01, 0.00000e+00])
[tensor(1280.), tensor(720.), tensor(1280.), tensor(720.)] tensor(0.54206) tensor(0.)
[tensor(1280.), tensor(720.), tensor(1280.), tensor(720.)] tensor(0.56291) tensor(27.)
[tensor(1280.), tensor(720.), tensor(1280.), tensor(720.)] tensor(0.79225) tensor(0.)
image 1/1 /home/andreas/Development/priv/cat-detection/yolov5/data/images/zidane.jpg: 640x640 2 persons, 1 tie, 1561.9ms
Speed: 2.7ms pre-process, 1561.9ms inference, 1.6ms NMS per image at shape (1, 3, 640, 640)
Results saved to runs/detect/exp38

As you can see it finds 2 people and 1 tie but the coordinates are not correct.

Here's the correct result with the onnx model:
zidane

In the readme it's stated that YOLOv7-tiny with Post-Process (NMS) ONNX to TFLite Float32 has been converted. Do you have the command and a "detection.py" what works with the result? Perhaps if I could get a hold of that I could solve this myself?

  1. I've tried different flags when converting but I really don't know if this is due to the model not beiung compatible with int8.
  2. I'm trying to convert a yolov7 model to tflite to be able to run inference on something called frigate and I've already written the infra to run yolov5 tflite models and would like to use the same code for yolov7, or with some changes but I need to figure out what to change.
  3. none

Seems can not handle Concat op

Issue Type

Others

Download URL for ONNX

model.zip

Description

I am trying to convert above model.onnx to tflite format by executing onnx2tf -i EyeNet.onnx, but it throw error at the concat op:
image
log:

Model loaded ========================================================================

Model convertion started ============================================================
INFO: input_op_name: input shape: [1, 1, 192, 192] dtype: float32

INFO: onnx_op_type: Conv onnx_op_name: Conv_0
INFO: input_name.1: input shape: [1, 1, 192, 192] dtype: float32
INFO: input_name.2: onnx::Conv_456 shape: [8, 1, 3, 3] dtype: <class 'numpy.float32'>
INFO: input_name.3: onnx::Conv_457 shape: [8] dtype: <class 'numpy.float32'>
INFO: output_name.1: input.4 shape: None dtype: None
INFO: tf_op_type: convolution_v2
INFO: input.1.input: name: tf.compat.v1.pad/Pad:0 shape: (1, 194, 194, 1) dtype: <dtype: 'float32'>
INFO: input.2.weights: shape: (3, 3, 1, 8) dtype: float32
INFO: input.3.bias: shape: (8,) dtype: float32
INFO: output.1.output: name: tf.math.add/Add:0 shape: (1, 96, 96, 8) dtype: <dtype: 'float32'>

INFO: onnx_op_type: LeakyRelu onnx_op_name: LeakyRelu_1
INFO: input_name.1: input.4 shape: None dtype: None
INFO: output_name.1: onnx::Conv_282 shape: None dtype: None
INFO: tf_op_type: leaky_relu
INFO: input.1.features: name: tf.math.add/Add:0 shape: (1, 96, 96, 8) dtype: <dtype: 'float32'>
INFO: input.2.alpha: val: 0.10000000149011612
INFO: output.1.output: name: tf.nn.leaky_relu/LeakyRelu:0 shape: (1, 96, 96, 8) dtype: <dtype: 'float32'>

INFO: onnx_op_type: Conv onnx_op_name: Conv_2
INFO: input_name.1: onnx::Conv_282 shape: None dtype: None
INFO: input_name.2: onnx::Conv_459 shape: [8, 1, 3, 3] dtype: <class 'numpy.float32'>
INFO: input_name.3: onnx::Conv_460 shape: [8] dtype: <class 'numpy.float32'>
INFO: output_name.1: input.12 shape: None dtype: None
INFO: tf_op_type: depthwise_conv2d_v2
INFO: input.1.input: name: tf.compat.v1.pad_1/Pad:0 shape: (1, 98, 98, 8) dtype: <dtype: 'float32'>
INFO: input.2.weights: shape: (3, 3, 8, 1) dtype: <dtype: 'float32'>
INFO: input.3.bias: shape: (8,) dtype: float32
INFO: output.1.output: name: tf.math.add_1/Add:0 shape: (1, 48, 48, 8) dtype: <dtype: 'float32'>

INFO: onnx_op_type: LeakyRelu onnx_op_name: LeakyRelu_3
INFO: input_name.1: input.12 shape: None dtype: None
INFO: output_name.1: onnx::Conv_285 shape: None dtype: None
INFO: tf_op_type: leaky_relu
INFO: input.1.features: name: tf.math.add_1/Add:0 shape: (1, 48, 48, 8) dtype: <dtype: 'float32'>
INFO: input.2.alpha: val: 0.10000000149011612
INFO: output.1.output: name: tf.nn.leaky_relu_1/LeakyRelu:0 shape: (1, 48, 48, 8) dtype: <dtype: 'float32'>

INFO: onnx_op_type: Conv onnx_op_name: Conv_4
INFO: input_name.1: onnx::Conv_285 shape: None dtype: None
INFO: input_name.2: onnx::Conv_462 shape: [16, 8, 1, 1] dtype: <class 'numpy.float32'>
INFO: input_name.3: onnx::Conv_463 shape: [16] dtype: <class 'numpy.float32'>
INFO: output_name.1: input.20 shape: None dtype: None
INFO: tf_op_type: convolution_v2
INFO: input.1.input: name: tf.nn.leaky_relu_1/LeakyRelu:0 shape: (1, 48, 48, 8) dtype: <dtype: 'float32'>
INFO: input.2.weights: shape: (1, 1, 8, 16) dtype: float32
INFO: input.3.bias: shape: (16,) dtype: float32
INFO: output.1.output: name: tf.math.add_2/Add:0 shape: (1, 48, 48, 16) dtype: <dtype: 'float32'>

INFO: onnx_op_type: LeakyRelu onnx_op_name: LeakyRelu_5
INFO: input_name.1: input.20 shape: None dtype: None
INFO: output_name.1: onnx::Conv_288 shape: None dtype: None
INFO: tf_op_type: leaky_relu
INFO: input.1.features: name: tf.math.add_2/Add:0 shape: (1, 48, 48, 16) dtype: <dtype: 'float32'>
INFO: input.2.alpha: val: 0.10000000149011612
INFO: output.1.output: name: tf.nn.leaky_relu_2/LeakyRelu:0 shape: (1, 48, 48, 16) dtype: <dtype: 'float32'>

INFO: onnx_op_type: Conv onnx_op_name: Conv_6
INFO: input_name.1: onnx::Conv_288 shape: None dtype: None
INFO: input_name.2: onnx::Conv_465 shape: [16, 1, 3, 3] dtype: <class 'numpy.float32'>
INFO: input_name.3: onnx::Conv_466 shape: [16] dtype: <class 'numpy.float32'>
INFO: output_name.1: input.28 shape: None dtype: None
INFO: tf_op_type: depthwise_conv2d_v2
INFO: input.1.input: name: tf.compat.v1.pad_2/Pad:0 shape: (1, 50, 50, 16) dtype: <dtype: 'float32'>
INFO: input.2.weights: shape: (3, 3, 16, 1) dtype: <dtype: 'float32'>
INFO: input.3.bias: shape: (16,) dtype: float32
INFO: output.1.output: name: tf.math.add_3/Add:0 shape: (1, 48, 48, 16) dtype: <dtype: 'float32'>

INFO: onnx_op_type: LeakyRelu onnx_op_name: LeakyRelu_7
INFO: input_name.1: input.28 shape: None dtype: None
INFO: output_name.1: onnx::Conv_291 shape: None dtype: None
INFO: tf_op_type: leaky_relu
INFO: input.1.features: name: tf.math.add_3/Add:0 shape: (1, 48, 48, 16) dtype: <dtype: 'float32'>
INFO: input.2.alpha: val: 0.10000000149011612
INFO: output.1.output: name: tf.nn.leaky_relu_3/LeakyRelu:0 shape: (1, 48, 48, 16) dtype: <dtype: 'float32'>

INFO: onnx_op_type: Conv onnx_op_name: Conv_8
INFO: input_name.1: onnx::Conv_291 shape: None dtype: None
INFO: input_name.2: onnx::Conv_468 shape: [16, 16, 1, 1] dtype: <class 'numpy.float32'>
INFO: input_name.3: onnx::Conv_469 shape: [16] dtype: <class 'numpy.float32'>
INFO: output_name.1: input.36 shape: None dtype: None
INFO: tf_op_type: convolution_v2
INFO: input.1.input: name: tf.nn.leaky_relu_3/LeakyRelu:0 shape: (1, 48, 48, 16) dtype: <dtype: 'float32'>
INFO: input.2.weights: shape: (1, 1, 16, 16) dtype: float32
INFO: input.3.bias: shape: (16,) dtype: float32
INFO: output.1.output: name: tf.math.add_4/Add:0 shape: (1, 48, 48, 16) dtype: <dtype: 'float32'>

INFO: onnx_op_type: LeakyRelu onnx_op_name: LeakyRelu_9
INFO: input_name.1: input.36 shape: None dtype: None
INFO: output_name.1: onnx::Conv_294 shape: None dtype: None
INFO: tf_op_type: leaky_relu
INFO: input.1.features: name: tf.math.add_4/Add:0 shape: (1, 48, 48, 16) dtype: <dtype: 'float32'>
INFO: input.2.alpha: val: 0.10000000149011612
INFO: output.1.output: name: tf.nn.leaky_relu_4/LeakyRelu:0 shape: (1, 48, 48, 16) dtype: <dtype: 'float32'>

INFO: onnx_op_type: Conv onnx_op_name: Conv_10
INFO: input_name.1: onnx::Conv_294 shape: None dtype: None
INFO: input_name.2: onnx::Conv_471 shape: [8, 16, 3, 3] dtype: <class 'numpy.float32'>
INFO: input_name.3: onnx::Conv_472 shape: [8] dtype: <class 'numpy.float32'>
INFO: output_name.1: onnx::Concat_470 shape: None dtype: None
INFO: tf_op_type: convolution_v2
INFO: input.1.input: name: tf.compat.v1.pad_3/Pad:0 shape: (1, 50, 50, 16) dtype: <dtype: 'float32'>
INFO: input.2.weights: shape: (3, 3, 16, 8) dtype: float32
INFO: input.3.bias: shape: (8,) dtype: float32
INFO: output.1.output: name: tf.math.add_5/Add:0 shape: (1, 48, 48, 8) dtype: <dtype: 'float32'>

INFO: onnx_op_type: Conv onnx_op_name: Conv_11
INFO: input_name.1: onnx::Conv_294 shape: None dtype: None
INFO: input_name.2: onnx::Conv_474 shape: [4, 16, 3, 3] dtype: <class 'numpy.float32'>
INFO: input_name.3: onnx::Conv_475 shape: [4] dtype: <class 'numpy.float32'>
INFO: output_name.1: input.48 shape: None dtype: None
INFO: tf_op_type: convolution_v2
INFO: input.1.input: name: tf.compat.v1.pad_4/Pad:0 shape: (1, 50, 50, 16) dtype: <dtype: 'float32'>
INFO: input.2.weights: shape: (3, 3, 16, 4) dtype: float32
INFO: input.3.bias: shape: (4,) dtype: float32
INFO: output.1.output: name: tf.math.add_6/Add:0 shape: (1, 48, 48, 4) dtype: <dtype: 'float32'>

INFO: onnx_op_type: LeakyRelu onnx_op_name: LeakyRelu_12
INFO: input_name.1: input.48 shape: None dtype: None
INFO: output_name.1: onnx::Conv_299 shape: None dtype: None
INFO: tf_op_type: leaky_relu
INFO: input.1.features: name: tf.math.add_6/Add:0 shape: (1, 48, 48, 4) dtype: <dtype: 'float32'>
INFO: input.2.alpha: val: 0.10000000149011612
INFO: output.1.output: name: tf.nn.leaky_relu_5/LeakyRelu:0 shape: (1, 48, 48, 4) dtype: <dtype: 'float32'>

INFO: onnx_op_type: Conv onnx_op_name: Conv_13
INFO: input_name.1: onnx::Conv_299 shape: None dtype: None
INFO: input_name.2: onnx::Conv_477 shape: [4, 4, 3, 3] dtype: <class 'numpy.float32'>
INFO: input_name.3: onnx::Conv_478 shape: [4] dtype: <class 'numpy.float32'>
INFO: output_name.1: onnx::Concat_476 shape: None dtype: None
INFO: tf_op_type: convolution_v2
INFO: input.1.input: name: tf.compat.v1.pad_5/Pad:0 shape: (1, 50, 50, 4) dtype: <dtype: 'float32'>
INFO: input.2.weights: shape: (3, 3, 4, 4) dtype: float32
INFO: input.3.bias: shape: (4,) dtype: float32
INFO: output.1.output: name: tf.math.add_7/Add:0 shape: (1, 48, 48, 4) dtype: <dtype: 'float32'>

INFO: onnx_op_type: Conv onnx_op_name: Conv_14
INFO: input_name.1: onnx::Conv_299 shape: None dtype: None
INFO: input_name.2: onnx::Conv_480 shape: [4, 4, 3, 3] dtype: <class 'numpy.float32'>
INFO: input_name.3: onnx::Conv_481 shape: [4] dtype: <class 'numpy.float32'>
INFO: output_name.1: input.60 shape: None dtype: None
INFO: tf_op_type: convolution_v2
INFO: input.1.input: name: tf.compat.v1.pad_6/Pad:0 shape: (1, 50, 50, 4) dtype: <dtype: 'float32'>
INFO: input.2.weights: shape: (3, 3, 4, 4) dtype: float32
INFO: input.3.bias: shape: (4,) dtype: float32
INFO: output.1.output: name: tf.math.add_8/Add:0 shape: (1, 48, 48, 4) dtype: <dtype: 'float32'>

INFO: onnx_op_type: LeakyRelu onnx_op_name: LeakyRelu_15
INFO: input_name.1: input.60 shape: None dtype: None
INFO: output_name.1: onnx::Conv_304 shape: None dtype: None
INFO: tf_op_type: leaky_relu
INFO: input.1.features: name: tf.math.add_8/Add:0 shape: (1, 48, 48, 4) dtype: <dtype: 'float32'>
INFO: input.2.alpha: val: 0.10000000149011612
INFO: output.1.output: name: tf.nn.leaky_relu_6/LeakyRelu:0 shape: (1, 48, 48, 4) dtype: <dtype: 'float32'>

INFO: onnx_op_type: Conv onnx_op_name: Conv_16
INFO: input_name.1: onnx::Conv_304 shape: None dtype: None
INFO: input_name.2: onnx::Conv_483 shape: [4, 4, 3, 3] dtype: <class 'numpy.float32'>
INFO: input_name.3: onnx::Conv_484 shape: [4] dtype: <class 'numpy.float32'>
INFO: output_name.1: onnx::Concat_482 shape: None dtype: None
INFO: tf_op_type: convolution_v2
INFO: input.1.input: name: tf.compat.v1.pad_7/Pad:0 shape: (1, 50, 50, 4) dtype: <dtype: 'float32'>
INFO: input.2.weights: shape: (3, 3, 4, 4) dtype: float32
INFO: input.3.bias: shape: (4,) dtype: float32
INFO: output.1.output: name: tf.math.add_9/Add:0 shape: (1, 48, 48, 4) dtype: <dtype: 'float32'>

INFO: onnx_op_type: Concat onnx_op_name: Concat_17
INFO: input_name.1: onnx::Concat_470 shape: None dtype: None
INFO: input_name.2: onnx::Concat_476 shape: None dtype: None
INFO: input_name.3: onnx::Concat_482 shape: None dtype: None
INFO: output_name.1: input.68 shape: None dtype: None
ERROR: The trace log is below.
Traceback (most recent call last):
  File "c:\users\user\appdata\local\programs\python\python38\lib\site-packages\onnx2tf\utils\common_functions.py", line 176, in print_wrapper_func
    result = func(*args, **kwargs)
  File "c:\users\user\appdata\local\programs\python\python38\lib\site-packages\onnx2tf\utils\common_functions.py", line 225, in inverted_operation_enable_disable_wrapper_func
    result = func(*args, **kwargs)
  File "c:\users\user\appdata\local\programs\python\python38\lib\site-packages\onnx2tf\ops\Concat.py", line 61, in make_node
    tensor_rank=len(shape),
TypeError: object of type 'NoneType' has no len()

[MobileBERT] Add `--optimization_for_gpu_delegate` option

Issue Type

Others

onnx2tf version number

1.5.24

onnx version number

1.13.0

tensorflow version number

2.10.0

Download URL for ONNX

  1. [MobileBERT] RecursionError: maximum recursion depth exceeded #130
  2. https://github.com/PINTO0309/onnx2tf/releases/download/1.1.28/fastestdet.onnx

Parameter Replacement JSON

  1. https://github.com/PINTO0309/onnx2tf/raw/main/json_samples/replace_MobileBERT.json

Description

[nanodet-plus] Conv layer shape wrong

Issue Type

Others

onnx2tf version number

1.1.22

Download URL for ONNX

https://drive.google.com/file/d/1HyUDTYSTgS7_Gs29SxUSoxs3irqvIsXM/view?usp=sharing

Parameter Replacement JSON

{}

Description

  1. Purpose: Personal development
  2. What:
    Running script: onnx2tf -i nanodet-plus-m_416.onnx -oh5 -k "data"
    Issue log:
INFO: onnx_op_type: Conv onnx_op_name: /backbone/stage2/stage2.1/branch2/branch2.0/Conv
INFO: input_name.1: /backbone/stage2/stage2.1/Split_output_1 shape: [1, 58, 52, 52] dtype: float32
INFO: input_name.2: onnx::Conv_1392 shape: [58, 58, 1, 1] dtype: <class 'numpy.float32'>
INFO: input_name.3: onnx::Conv_1393 shape: [58] dtype: <class 'numpy.float32'>
INFO: output_name.1: /backbone/stage2/stage2.1/branch2/branch2.0/Conv_output_0 shape: [1, 58, 52, 52] dtype: float32
ERROR: The trace log is below.
Traceback (most recent call last):
  File "/mnt/hdd10tb/Users/thaovu/.conda/envs/onnx2tf/lib/python3.8/site-packages/onnx2tf/utils/common_functions.py", line 261, in print_wrapper_func
    result = func(*args, **kwargs)
  File "/mnt/hdd10tb/Users/thaovu/.conda/envs/onnx2tf/lib/python3.8/site-packages/onnx2tf/utils/common_functions.py", line 323, in inverted_operation_enable_disable_wrapper_func
    result = func(*args, **kwargs)
  File "/mnt/hdd10tb/Users/thaovu/.conda/envs/onnx2tf/lib/python3.8/site-packages/onnx2tf/ops/Conv.py", line 153, in make_node
    tf.nn.convolution(
  File "/mnt/hdd10tb/Users/thaovu/.conda/envs/onnx2tf/lib/python3.8/site-packages/tensorflow/python/util/traceback_utils.py", line 153, in error_handler
    raise e.with_traceback(filtered_tb) from None
  File "/mnt/hdd10tb/Users/thaovu/.conda/envs/onnx2tf/lib/python3.8/site-packages/keras/layers/core/tf_op_layer.py", line 119, in handle
    return TFOpLambda(op)(*args, **kwargs)
  File "/mnt/hdd10tb/Users/thaovu/.conda/envs/onnx2tf/lib/python3.8/site-packages/keras/utils/traceback_utils.py", line 70, in error_handler
    raise e.with_traceback(filtered_tb) from None
ValueError: Exception encountered when calling layer "tf.nn.convolution_4" (type TFOpLambda).

Depth of input (52) is not a multiple of input depth of filter (58) for '{{node tf.nn.convolution_4/convolution}} = Conv2D[T=DT_FLOAT, data_format="NHWC", dilations=[1, 1, 1, 1], explicit_paddings=[], padding="SAME", strides=[1, 1, 1, 1], use_cudnn_on_gpu=true](Placeholder, tf.nn.convolution_4/convolution_internal/filters)' with input shapes: [1,58,52,52], [1,1,58,58].

Question 1: Shape of onnx::Conv_1393 layer must be [58,58,1,1], but in here it is [1,1,58,58]. Any cause from data_format?
Question 2: I set -k "data" but data_format of this layer is NHWC. Correct me if I wrong
3. How:
Firstly, I ran without -k option and get the same issue, I tried to use -k but still the the same
I also check parameter replacement but cant find suitable solution.
4. Why: I wanna convert NanoDet-plus-m model to Keras (h5 format)
5. Resources: Pytorch model from NanoDet model zoo https://github.com/RangiLyu/nanodet
Thank you for your hard working. Your projects are very useful <3

[YOLOX-X] [Faster-RCNN] [YOLOv7] TensorFlow aborts when exporting a model with NMS to Keras (.h5)

Issue Type

Others

onnx2tf version number

1.5.29

onnx version number

1.13.0

tensorflow version number

2.10.0

Download URL for ONNX

https://s3.ap-northeast-2.wasabisys.com/temp-models/onnx2tf_146/yolox_x.onnx
https://github.com/PINTO0309/onnx2tf/releases/download/1.1.28/faster_rcnn-10.onnx
https://github.com/PINTO0309/onnx2tf/releases/download/1.1.28/yolov7_tiny_head_0.768_post_480x640.onnx

Parameter Replacement JSON

N/A

Description

  1. Personal
  2. TensorFlow aborts when exporting a model with NMS to Keras (.h5)
    onnx2tf -i yolox_x.onnx -oh5
    
    ValueError: This Keras op layer was generated from <function non_max_suppression at 0x7fd6a96ff1f0>,
    a method that is not publicly exposed in the TensorFlow API.
    This may have happened if the method was explicitly decorated to add dispatching support,
    and it was used during Functional model construction.
    To ensure cross-version compatibility of Keras models that use op layers,
    only op layers produced from public TensorFlow API symbols can be serialized.
    
    image

Mult Operation failing if mul operation is after Unsqueeze operation citing dimension mismatch. The Input from Unsqueeze operation is not transformed when passed as input to Mult

Issue Type

Others

Download URL for ONNX

https://act-my.sharepoint.com/personal/WopiFrame.aspx?sourcedoc=%7B35bd9e06-60e0-471a-b50b-7743073d62b8%7D&action=default

Description

Log added below: One of the inputs. to Mul operation is transformed and the one from Unsqueeze is not transformed

INFO: tf_op_type: Unsqueeze input_name.1: tf.math.divide_1/truediv:0 shape: (1, 72) dtype: <dtype: 'float32'>
INFO: tf_op_type: Unsqueeze output_name.1: 466 shape: (1, 72, 1) dtype: <dtype: 'float32'>
starting to make node
<module 'onnx2tf.ops.Unsqueeze' from '~/onnx2tf/onnx2tf/ops/Unsqueeze.py'>

INFO: onnx_op_type: Unsqueeze onnx_op_name: Unsqueeze_49
INFO: input_name.1: 466 shape: [1, 72, 1] dtype: float32
INFO: output_name.1: 467 shape: [1, 72, 1, 1] dtype: float32
[3]
[1, 72, 1, 1]
[1, 72, 1, 1]
tf.reshape_1/Reshape:0
(1, 72, 1, 1)
True
INFO: tf_op_type: Unsqueeze input_name.1: tf.reshape/Reshape:0 shape: (1, 72, 1) dtype: <dtype: 'float32'>
INFO: tf_op_type: Unsqueeze output_name.1: 467 shape: (1, 72, 1, 1) dtype: <dtype: 'float32'>
starting to make node
<module 'onnx2tf.ops.Mul' from '~/onnx2tf/onnx2tf/ops/Mul.py'>

INFO: onnx_op_type: Mul onnx_op_name: Mul_50
INFO: input_name.1: 453 shape: [1, 72, 60, 60] dtype: float32
INFO: input_name.2: 467 shape: [1, 72, 1, 1] dtype: float32
INFO: output_name.1: 468 shape: [1, 72, 60, 60] dtype: float32
[1, 72, 60, 60]

Traceback (most recent call last):
File “/onnx2tf/onnx2tf/utils/common_functions.py", line 49, in print_wrapper_func
result = func(*args, **kwargs)
File "
/onnx2tf/onnx2tf/utils/common_functions.py", line 105, in inverted_operation_enable_disable_wrapper_func
result = func(*args, **kwargs)
File “/onnx2tf/onnx2tf/ops/Mul.py", line 61, in make_node
tf.math.multiply(
File "
/model-conversion/git/env/lib/python3.8/site-packages/tensorflow/python/util/traceback_utils.py", line 153, in error_handler
raise e.with_traceback(filtered_tb) from None
File "/model-conversion/git/env/lib/python3.8/site-packages/keras/layers/core/tf_op_layer.py", line 119, in handle
return TFOpLambda(op)(*args, **kwargs)
File "
/model-conversion/git/env/lib/python3.8/site-packages/keras/utils/traceback_utils.py", line 70, in error_handler
raise e.with_traceback(filtered_tb) from None
ValueError: Exception encountered when calling layer "tf.math.multiply_3" (type TFOpLambda).

Dimensions must be equal, but are 60 and 72 for '{{node tf.math.multiply_3/Mul}} = Mul[T=DT_FLOAT](Placeholder, Placeholder_1)' with input shapes: [1,60,60,72], [1,72,1,1].

Call arguments received by layer "tf.math.multiply_3" (type TFOpLambda):
• x=tf.Tensor(shape=(1, 60, 60, 72), dtype=float32)
• y=tf.Tensor(shape=(1, 72, 1, 1), dtype=float32)
• name='Mul_50'

[TODO] NMS implementation for GPU Delegate / TPU support

Issue Type

Others

onnx2tf version number

1.7.x

onnx version number

1.13.1

tensorflow version number

2.10.0

Download URL for ONNX

postprocess_8400.onnx.zip

Parameter Replacement JSON

N/A

Description

  1. Personal
  2. [TODO] NMS implementation for GPU Delegate support
    Generate full-scratch, redundant NMS blocks without NonMaxSuppressionV4. Perform this workaround only when the --replace_nonmaxsuppression_to_pseudo_nonmaxsuppression option is enabled. However, this model customization should originally be implemented on the PyTorch side of the PyTorch implementation that exports ONNX. Therefore, the full-scratch NMS processing group generated by this tool is a very specific custom that can only be used for very limited models.

Ref: Yolov7-tiny to TensorflowLite comversion results in a dynamic output model incompatible with TfLite Java API #159

In the savedModel, proper signatures are not getting added (issue exists for resnet18 as well)

Issue Type

Others

Download URL for ONNX

Default resnet18-v1-7.onnx has the same issue

Description

I used example resnet onnx model... The converted saved model doesnt have required Signatures. I was trying. to use tensorflowjs_converter to convert it into tfjs model and it was failing in the missing signatures.

heres is the. output. of saved_model_cli for resnet:

MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['__saved_model_init_op']:
The given SavedModel SignatureDef contains the following input(s):
The given SavedModel SignatureDef contains the following output(s):
outputs['__saved_model_init_op'] tensor_info:
dtype: DT_INVALID
shape: unknown_rank
name: NoOp
Method name is:

[face_recognition_sface] [Query][tflite]The tool or cmdline to support NCHW between NHWC

Issue Type

Others

onnx2tf version number

1.0.48

Download URL for ONNX

Model:
1.face_recognition_sface_2021dec.onnx
2.https://github.com/opencv/opencv_zoo/blob/master/models/face_recognition_sface/face_recognition_sface_2021dec-act_int8-wt_int8-quantized.onnx

Parameter Replacement JSON

NA

Description

Hi @PINTO0309
Thanks for your job and it working when ONNX covert to tflite without quantizion.
for quantized ONNX model or tflite model, the tool seems not working for belwo error
ERROR: QLinearMul OP is not yet implemented.

so i would like to check with you
whether the tool support NCHW between NHWC convertion for tflite model?

Thanks

Weird bug in RoiAlign

Issue Type

Others

onnx2tf version number

1.1.33

Download URL for ONNX

Please use code below to reproduce bug.

import torch
from einops import rearrange
from torch import nn
import numpy as np
from onnx2tf import convert
import shutil
import torchvision
import tensorflow as tf
import cv2
import matplotlib.pyplot as plt
import requests
import onnxruntime


class dummy_network(nn.Module):

    def __init__(self, output_size, spatial_scale):
        super(dummy_network, self).__init__()

        self.output_size = output_size
        self.spatial_scale = spatial_scale

    def forward(self, x, roi):
        x = torchvision.ops.roi_align(x, boxes=roi, output_size=self.output_size, spatial_scale=self.spatial_scale)

        return x


def visualize(outputs):
    plt.figure(figsize=(8, 8))

    col = 4
    row = len(outputs) // 4 + 1

    for i, o in enumerate(outputs, start=1):
        plt.subplot(row, col, i)
        plt.axis("off")
        plt.imshow(o.astype(int))

    plt.show()


def main():
    model = dummy_network(output_size=(64, 64), spatial_scale=1.0)
    model.eval()

    dummy_name = "dummy_roi_align"
    onnx_save_path = f"tflite/{dummy_name}.onnx"
    temp_tflite = "tflite/model_float32.tflite"
    tflite_save_path = f"tflite/{dummy_name}.tflite"

    url = "https://static01.nyt.com/images/2021/09/14/science/07CAT-STRIPES/07CAT-STRIPES-mediumSquareAt3X-v2.jpg"
    image_nparray = np.asarray(bytearray(requests.get(url).content), dtype=np.uint8)
    image = cv2.imdecode(image_nparray, cv2.IMREAD_COLOR)

    dummy_input_x = np.expand_dims(cv2.resize(cv2.cvtColor(image, cv2.COLOR_BGR2RGB), (256, 256)), axis=0)
    dummy_input_x = rearrange(torch.Tensor(dummy_input_x), "n h w c -> n c h w")
    # dummy_input_roi = [[0, 0, 0, 0.5, 0.5], [0, 0.5, 0, 1, 0.5], [0, 0, 0.5, 0.5, 1], [0, 0.5, 0.5, 1, 1]]
    dummy_input_roi = [[0, 0, 0, 128, 128], [0, 128, 0, 256, 128], [0, 0, 128, 128, 256], [0, 128, 128, 256, 256]]
    # dummy_input_roi = [[0, 0, 1, 2, 3]]
    # dummy_input_roi = [[0, 0, 0, 256, 256]]
    dummy_input_roi = torch.Tensor(dummy_input_roi)

    torch.onnx.export(model,
                      args=(dummy_input_x, dummy_input_roi),
                      f=onnx_save_path,
                      input_names=["x", "roi"],
                      opset_version=11)

    convert(onnx_save_path, output_folder_path="tflite")
    shutil.move(temp_tflite, tflite_save_path)


    # get torch output
    # -----------------------------------------------------------------------------------------------
    with torch.no_grad():
        torch_output = model(dummy_input_x, dummy_input_roi)
        torch_output = rearrange(torch_output, "n c h w -> n h w c")

    # visualize(torch_output)

    # get onnx output
    # -----------------------------------------------------------------------------------------------
    onnx_session = onnxruntime.InferenceSession(onnx_save_path, providers=['CPUExecutionProvider'])
    onnx_inputs = dict(x=dummy_input_x.numpy(), roi=dummy_input_roi.numpy())
    onnx_output = onnx_session.run(None, onnx_inputs)[0]
    onnx_output = rearrange(onnx_output, "n c h w -> n h w c")

    # compare torch output and onnx output
    np.testing.assert_allclose(onnx_output, torch_output.numpy())

    # get tflite output
    # -----------------------------------------------------------------------------------------------
    tflite_onnx2tf = tf.lite.Interpreter(model_path=tflite_save_path)
    tflite_onnx2tf.allocate_tensors()
    tflite_onnx2tf.set_tensor(tflite_onnx2tf.get_input_details()[0]['index'],
                              rearrange(dummy_input_x.numpy(), "n c h w -> n h w c"))
    tflite_onnx2tf.set_tensor(tflite_onnx2tf.get_input_details()[1]['index'], dummy_input_roi.numpy())
    tflite_onnx2tf.invoke()
    tflite_output = [tflite_onnx2tf.get_tensor(i['index']) for i in tflite_onnx2tf.get_output_details()][0]

    total_output = np.concatenate([torch_output, onnx_output, tflite_output], axis=0)
    visualize(total_output)

    print("convert done")

    return


if __name__ == "__main__":
    main()

Parameter Replacement JSON

N/A

Description

  1. Purpose: Personal development

  2. What: When I tested RoiAlign, it showed unexpected output as below.

pytorch output
onnx output
wrong tflite output
correct tflite output after modification
  1. How: After some debugging, I found that wrong order of roi coordinates are fed into tflite. After changing line 124 to x0, x1, y1, y0 = tf.split(boxes, 4, axis=1), I could get correct result as shown above.
    def transform_fpcoor_for_tf(
    *,
    boxes,
    image_shape,
    crop_size,
    sampling_ratio,
    adaptive_ratio,
    ):
    x0, y0, x1, y1 = tf.split(boxes, 4, axis=1)
    if not adaptive_ratio:
    crop_shape = (

    Although the output value is slightly different as below due to the implementation detail in tensorflow, it looks working fine. However, I couldn't find out why order of the roi coordinates is changed from x0, y0, x1, y1 to x0, x1, y1, y0.

[human_segmentation_pphumanseg_2021oct] `-cotof` option is used, ValueError: Output tensors of a Functional model must be the output of a TensorFlow Layer

Issue Type

Others

onnx2tf version number

1.5.7

onnx version number

1.13.0

tensorflow version number

2.10.0

Download URL for ONNX

https://github.com/PINTO0309/onnx2tf/releases/download/1.1.27/human_segmentation_pphumanseg_2021oct.onnx

Parameter Replacement JSON

{
  "format_version": 1,
  "operations": [
    {
      "op_name": "Resize_0",
      "param_target": "inputs",
      "param_name": "Concat_0",
      "values": [48,48]
    },
    {
      "op_name": "Resize_1",
      "param_target": "inputs",
      "param_name": "Concat_1",
      "values": [48,48]
    },
    {
      "op_name": "Resize_2",
      "param_target": "inputs",
      "param_name": "Concat_2",
      "values": [48,48]
    },
    {
      "op_name": "Resize_3",
      "param_target": "inputs",
      "param_name": "Concat_3",
      "values": [24,24]
    },
    {
      "op_name": "Resize_4",
      "param_target": "inputs",
      "param_name": "Concat_4",
      "values": [48,48]
    },
    {
      "op_name": "Resize_5",
      "param_target": "inputs",
      "param_name": "Concat_5",
      "values": [48,48]
    },
    {
      "op_name": "Resize_6",
      "param_target": "inputs",
      "param_name": "Concat_6",
      "values": [48,48]
    },
    {
      "op_name": "Resize_7",
      "param_target": "inputs",
      "param_name": "Concat_7",
      "values": [24,24]
    },
    {
      "op_name": "Resize_8",
      "param_target": "inputs",
      "param_name": "Concat_8",
      "values": [24,24]
    },
    {
      "op_name": "Resize_9",
      "param_target": "inputs",
      "param_name": "Concat_9",
      "values": [12,12]
    },
    {
      "op_name": "Resize_10",
      "param_target": "inputs",
      "param_name": "Concat_10",
      "values": [48,48]
    },
    {
      "op_name": "Resize_11",
      "param_target": "inputs",
      "param_name": "Concat_11",
      "values": [48,48]
    },
    {
      "op_name": "Resize_12",
      "param_target": "inputs",
      "param_name": "Concat_12",
      "values": [48,48]
    },
    {
      "op_name": "Resize_13",
      "param_target": "inputs",
      "param_name": "Concat_14",
      "values": [192,192]
    },
    {
      "op_name": "Reshape_0",
      "param_target": "outputs",
      "param_name": "Reshape_0",
      "post_process_transpose_perm": [0,2,3,1]
    },
    {
      "op_name": "Reshape_1",
      "param_target": "outputs",
      "param_name": "Reshape_1",
      "post_process_transpose_perm": [0,2,3,1]
    },
    {
      "op_name": "Transpose_0",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "Transpose_1",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "Softmax_0",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    }
  ]
}

Description

If the output layer to be verified is a Constant value, Keras appears to Abort. Therefore, we believe that Constant and ConstantOfShape should be excluded from the validation target. This should not be determined by the type of OP, but rather by whether it is an np.ndarray, in order to make a generic description.

onnx2tf -i human_segmentation_pphumanseg_2021oct.onnx -prf replace.json -cotof -cotoa 1e-1
ValueError: Output tensors of a Functional model must be the output of a TensorFlow `Layer` (thus holding past layer metadata). Found: [[[[-4.45949372e-05  2.32167356e-03  3.76565661e-03 ...

image

[human_segmentation_pphumanseg] Conversion error in human_segmentation_pphumanseg_2021oct.onnx

Issue Type

Others

onnx2tf version number

1.1.11

Download URL for ONNX

https://github.com/opencv/opencv_zoo/tree/master/models/human_segmentation_pphumanseg

Parameter Replacement JSON

{
  "format_version": 1,
  "operations": [
    {
      "op_name": "Resize_0",
      "param_target": "inputs",
      "param_name": "Concat_0",
      "values": [48,48]
    },
    {
      "op_name": "Resize_1",
      "param_target": "inputs",
      "param_name": "Concat_1",
      "values": [48,48]
    },
    {
      "op_name": "Resize_2",
      "param_target": "inputs",
      "param_name": "Concat_2",
      "values": [48,48]
    },
    {
      "op_name": "Resize_3",
      "param_target": "inputs",
      "param_name": "Concat_3",
      "values": [24,24]
    },
    {
      "op_name": "Resize_4",
      "param_target": "inputs",
      "param_name": "Concat_4",
      "values": [48,48]
    },
    {
      "op_name": "Resize_5",
      "param_target": "inputs",
      "param_name": "Concat_5",
      "values": [48,48]
    },
    {
      "op_name": "Resize_6",
      "param_target": "inputs",
      "param_name": "Concat_6",
      "values": [48,48]
    },
    {
      "op_name": "Resize_7",
      "param_target": "inputs",
      "param_name": "Concat_7",
      "values": [24,24]
    },
    {
      "op_name": "Resize_8",
      "param_target": "inputs",
      "param_name": "Concat_8",
      "values": [24,24]
    },
    {
      "op_name": "Resize_9",
      "param_target": "inputs",
      "param_name": "Concat_9",
      "values": [12,12]
    },
    {
      "op_name": "Resize_10",
      "param_target": "inputs",
      "param_name": "Concat_10",
      "values": [48,48]
    },
    {
      "op_name": "Resize_11",
      "param_target": "inputs",
      "param_name": "Concat_11",
      "values": [48,48]
    },
    {
      "op_name": "Resize_12",
      "param_target": "inputs",
      "param_name": "Concat_12",
      "values": [48,48]
    },
    {
      "op_name": "Resize_13",
      "param_target": "inputs",
      "param_name": "Concat_14",
      "values": [192,192]
    },
    {
      "op_name": "Reshape_0",
      "param_target": "outputs",
      "param_name": "Reshape_0",
      "post_process_transpose_perm": [0,2,3,1]
    },
    {
      "op_name": "Reshape_1",
      "param_target": "outputs",
      "param_name": "Reshape_1",
      "post_process_transpose_perm": [0,2,3,1]
    },
    {
      "op_name": "Transpose_0",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "Transpose_1",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "Softmax_0",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    }
  ]
}

Description

  1. I get an error when I run onnxsim. Converting a model without running onnxsim results in an error.
  • Error message
    onnx2tf -i human_segmentation_pphumanseg_2021oct.onnx
    Traceback (most recent call last):
      File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
        return _run_code(code, main_globals, None,
      File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
        exec(code, run_globals)
      File "/home/xxxxx/.vscode/extensions/ms-python.python-2022.16.1/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 39, in <module>
        cli.main()
      File "/home/xxxxx/.vscode/extensions/ms-python.python-2022.16.1/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 430, in main
        run()
      File "/home/xxxxx/.vscode/extensions/ms-python.python-2022.16.1/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 284, in run_file
        runpy.run_path(target, run_name="__main__")
      File "/home/xxxxx/.vscode/extensions/ms-python.python-2022.16.1/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 321, in run_path
        return _run_module_code(code, init_globals, run_name,
      File "/home/xxxxx/.vscode/extensions/ms-python.python-2022.16.1/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 135, in _run_module_code
        _run_code(code, mod_globals, init_globals,
      File "/home/xxxxx/.vscode/extensions/ms-python.python-2022.16.1/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 124, in _run_code
        exec(code, run_globals)
      File "/home/xxxxx/git/onnx2tf/onnx2tf/onnx2tf.py", line 1142, in <module>
        main()
      File "/home/xxxxx/git/onnx2tf/onnx2tf/onnx2tf.py", line 1108, in main
        model = convert(
      File "/home/xxxxx/git/onnx2tf/onnx2tf/onnx2tf.py", line 558, in convert
        concrete_func = run_model.get_concrete_function(
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/eager/def_function.py", line 1239, in get_concrete_function
        concrete = self._get_concrete_function_garbage_collected(*args, **kwargs)
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/eager/def_function.py", line 1219, in _get_concrete_function_garbage_collected
        self._initialize(args, kwargs, add_initializers_to=initializers)
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/eager/def_function.py", line 785, in _initialize
        self._stateful_fn._get_concrete_function_internal_garbage_collected(  # pylint: disable=protected-access
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/eager/function.py", line 2523, in _get_concrete_function_internal_garbage_collected
        graph_function, _ = self._maybe_define_function(args, kwargs)
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/eager/function.py", line 2760, in _maybe_define_function
        graph_function = self._create_graph_function(args, kwargs)
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/eager/function.py", line 2670, in _create_graph_function
        func_graph_module.func_graph_from_py_func(
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/framework/func_graph.py", line 1247, in func_graph_from_py_func
        func_outputs = python_func(*func_args, **func_kwargs)
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/eager/def_function.py", line 677, in wrapped_fn
        out = weak_wrapped_fn().__wrapped__(*args, **kwds)
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/framework/func_graph.py", line 1233, in autograph_handler
        raise e.ag_error_metadata.to_exception(e)
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/framework/func_graph.py", line 1222, in autograph_handler
        return autograph.converted_call(
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/autograph/impl/api.py", line 439, in converted_call
        result = converted_f(*effective_args, **kwargs)
      File "/tmp/__autograph_generated_fileldpw01_n.py", line 6, in <lambda>
        tf__lam = (lambda *inputs: ag__.with_function_scope((lambda lscope: ag__.converted_call(model, (inputs,), None, lscope)), 'lscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)))
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/autograph/core/function_wrappers.py", line 113, in with_function_scope
        return thunk(scope)
      File "/tmp/__autograph_generated_fileldpw01_n.py", line 6, in <lambda>
        tf__lam = (lambda *inputs: ag__.with_function_scope((lambda lscope: ag__.converted_call(model, (inputs,), None, lscope)), 'lscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)))
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/autograph/impl/api.py", line 377, in converted_call
        return _call_unconverted(f, args, kwargs, options)
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/autograph/impl/api.py", line 459, in _call_unconverted
        return f(*args)
      File "/usr/local/lib/python3.8/dist-packages/keras/utils/traceback_utils.py", line 70, in error_handler
        raise e.with_traceback(filtered_tb) from None
      File "/usr/local/lib/python3.8/dist-packages/tensorflow/python/framework/ops.py", line 1969, in _create_c_op
        raise ValueError(e.message)
    ValueError: in user code:
    
        File "/home/xxxxx/git/onnx2tf/onnx2tf/onnx2tf.py", line 557, in None  *
            lambda *inputs : model(inputs)
        File "/usr/local/lib/python3.8/dist-packages/keras/utils/traceback_utils.py", line 70, in error_handler  **
            raise e.with_traceback(filtered_tb) from None
    
        ValueError: Exception encountered when calling layer "tf.math.add_6" "                 f"(type TFOpLambda).
        
        Dimensions must be equal, but are 48 and 24 for '{{node model/tf.math.add_6/Add}} = AddV2[T=DT_FLOAT](model/tf.nn.relu_13/Relu, model/tf.image.resize/resize/ResizeBilinear)' with input shapes: [1,48,48,16], [1,24,48,16].
        
        Call arguments received by layer "tf.math.add_6" "                 f"(type TFOpLambda):
          • x=tf.Tensor(shape=(1, 48, 48, 16), dtype=float32)
          • y=tf.Tensor(shape=(1, 24, 48, 16), dtype=float32)
          • name='Add_5'
    
  1. To avoid conversion errors, write replace.json and run it.
    onnx2tf -i human_segmentation_pphumanseg_2021oct.onnx -prf replace.json

The final conversion was successful. https://github.com/PINTO0309/onnx2tf/releases/tag/1.1.12

[MobileFormer] Converted model outputs values mismatch with original ones.

Issue Type

Others

onnx2tf version number

1.4.2

onnx version number

1.12.0

tensorflow version number

2.10.1

Download URL for ONNX

9e: https://drive.google.com/file/d/1vGzO9MZGX-yGz6ATm4yHVJMASZACuy2t/view?usp=share_link
9t: https://drive.google.com/file/d/1Be-a7Pmo6auyAXHChAhC1qtzkbr5EytJ/view?usp=share_link
12e: https://drive.google.com/file/d/1NR-0Fm5q_ludb5MTWgDPzRzqDFKOsCkw/view?usp=share_link
12t: https://drive.google.com/file/d/1ReuXt4gpbq6O7mpUGudaBKjb6wI43ka6/view?usp=share_link

Parameter Replacement JSON

9e: https://drive.google.com/file/d/1av1dL5sWfjlghf2-jtmws-CR-Tcv_WIP/view?usp=share_link
9t: https://drive.google.com/file/d/18qB_VOW4Xp9PvCN4G-eRRnWhh9o1fF3F/view?usp=share_link
12e: https://drive.google.com/file/d/1T0nTkb5Szf9XjaNVVBTzzu5C9dgOURLd/view?usp=share_link
12t: https://drive.google.com/file/d/1JhH8K64PDJong4qkZbzyyJDKRExE7wlJ/view?usp=share_link

Description

Hi Master PINTO,

Following up on issue #103, the output values of the converted model are different from what is expected. I tried to export the pytorch model in both eval and training and both opset==9 and opset==12. For the models and parameter replacement files, the number stands for opset version, e stands for eval, and t stands for training. This issue happens in all scenarios. Please see this notebook https://drive.google.com/file/d/1mErgTLFiYGTgUnMRDcNp3HkOkSWa96aL/view?usp=share_link to replicate and for issue demonstration.

Also, I see that since you match every onnx operation with a basic tf operation, is there no way to restore those weights as trainable parameters? Thank you so much for keep digging in on this. If you need the original pytorch model or anything else I could assist on, let me know!

The output dimensions of Maxpool2D do not correspond

Issue Type

Others

onnx2tf version number

1.3.6

onnx version number

1.12.0

tensorflow version number

2.10.0

Download URL for ONNX

INFO: onnx_op_type: MaxPool onnx_op_name: MaxPool_346
INFO: input_name.1: 645 shape: [1, 64, 22, 22] dtype: float32
INFO: output_name.1: 646 shape: [1, 64, 10, 10] dtype: float32
INFO: tf_op_type: max_pool_v2
INFO: input.1.input: name: tf.nn.relu_16/Relu:0 shape: (1, 64, 22, 22) dtype: <dtype: 'float32'>
INFO: input.2.filters:
INFO: input.3.kernel_shape: val: [3, 3]
INFO: input.4.strides: val: [2, 2]
INFO: input.5.dilations: val: [1, 1]
INFO: input.6.padding: val: VALID
INFO: input.7.ceil_mode: val: False
INFO: output.1.output: name: tf.nn.max_pool2d_5/MaxPool2d:0 shape: (1, 31, 10, 22) dtype: <dtype: 'float32'>

Parameter Replacement JSON

{
  "format_version": 1,
  "operations": [
    {
      "op_name": "Reshape_54",
      "param_target": "outputs",
      "param_name": "173",
      "post_process_transpose_perm": [
        0,
        2,
        3,
        1
      ]
    },
    {
      "op_name": "Reshape_73",
      "param_target": "outputs",
      "param_name": "205",
      "post_process_transpose_perm": [
        0,
        2,
        3,
        1
      ]
    },
    {
      "op_name": "Reshape_92",
      "param_target": "outputs",
      "param_name": "237",
      "post_process_transpose_perm": [
        0,
        2,
        3,
        1
      ]
    },
    {
      "op_name": "Relu_306",
      "param_target": "outputs",
      "param_name": "576",
      "post_process_transpose_perm": [
        0,
        2,
        3,
        1
      ]
    },
    {
      "op_name": "Relu_150",
      "param_target": "outputs",
      "param_name": "320",
      "post_process_transpose_perm": [
        0,
        2,
        3,
        1
      ]
    }
  ]
}

Description

In onnx model, the output of MaxPool2D is [1, 64, 10, 10], but onnx2tf output (1, 31, 10, 22). I don't know what went wrong. Maybe should I transpose the input of MaxPool2D?

Problem in padding in ConvTranspose when stride is larger than 1

Issue Type

Others

onnx2tf version number

1.2.14

onnx version number

1.12.0

tensorflow version number

2.10

Download URL for ONNX

Please use code below for debugging.

import os
import shutil

import onnxruntime
import tensorflow as tf
import torch
from einops import rearrange
from torch import nn
import numpy as np
from onnx2tf import convert


class dummy_network(nn.Module):

    def __init__(self):
        super(dummy_network, self).__init__()

        # segmentation fault with wrong padding mode
        # self.tconv = torch.nn.ConvTranspose2d(3, 64, (3, 3), stride=3, padding=1)     # case 1: wrong output
        # self.tconv = torch.nn.ConvTranspose2d(3, 64, (4, 4), stride=3, padding=1)     # case 1: wrong output
        self.tconv = torch.nn.ConvTranspose2d(3, 64, (3, 3), stride=2, padding=1)       # case 2: segmentation fault

    def forward(self, x):
        result = self.tconv(x)

        return result


def main():
    os.makedirs("tflite", exist_ok=True)
    model = dummy_network()
    model.eval()

    dummy_name = "dummy_tconv"
    onnx_save_path = f"tflite/{dummy_name}.onnx"
    temp_tflite = "tflite/model_float32.tflite"
    tflite_save_path = f"tflite/{dummy_name}.tflite"

    # y1, x1, y2, x2
    dummy_x = torch.randn(1, 3, 224, 224, dtype=torch.float32)

    torch.onnx.export(model,
                      args=(dummy_x,),
                      f=onnx_save_path,
                      input_names=["x"],
                      output_names=["result"],
                      opset_version=11)

    # get onnx output
    # -----------------------------------------------------------------------------------------------
    onnx_session = onnxruntime.InferenceSession(onnx_save_path, providers=['CPUExecutionProvider'])
    onnx_inputs = dict(x=dummy_x.numpy())
    onnx_outputs = onnx_session.run(None, onnx_inputs)

    convert(onnx_save_path, output_folder_path="tflite")
    shutil.move(temp_tflite, tflite_save_path)

    # get tflite output
    # -----------------------------------------------------------------------------------------------
    tflite_onnx2tf = tf.lite.Interpreter(model_path=tflite_save_path)
    tflite_onnx2tf.allocate_tensors()
    tflite_onnx2tf.set_tensor(tflite_onnx2tf.get_input_details()[0]['index'],
                              rearrange(dummy_x.numpy(), "n c h w -> n h w c"))
    tflite_onnx2tf.invoke()
    tflite_output = [tflite_onnx2tf.get_tensor(i['index']) for i in tflite_onnx2tf.get_output_details()][0]

    np.testing.assert_allclose(rearrange(tflite_output, "n h w c -> n c h w"), onnx_outputs[0], atol=1e-3, rtol=0.5)

    print("convert done")

    return


if __name__ == "__main__":
    main()

Parameter Replacement JSON

N/A

Description

  1. Purpose: Personal development

  2. What: Converted tflite contains wrong padding mode which occurs wrong output or segmentation fault when stride is larger than 2 and padding is larger than 0.

  3. How:

    pad_mode = 'VALID'
    if auto_pad == 'NOTSET':
    if graph_node_input_shape[2:] == graph_node_output_shape[2:]:
    pad_mode = "SAME"
    else:
    # TODO: check for valid explicit pads.
    pad_mode = 'VALID'

    In ConvTranspose.py line number 105, padding mode is set to SAME by comparing input shape and output shape. However, output shape is not same with input shape when stride is not 1.
    With the code in above section, the outputs between onnx and tflite are different. Even worse, sometimes converted tflite failed to invoke due to segmentation fault, for example, when kernel is (3, 3), stride is (2, 2), padding is (1, 1).
    By replacing line number 105 with [int((s * (i - 1) + k - o) / 2) for s, i, k, o in zip(strides, graph_node_input_shape[2:], kernel_shape, graph_node_output_shape[2:])] == pads[1:3], the error is gone in some cases. But this is not enough, there should be more lines of code for comparing given padding value and calculated padding value to check padding mode is SAME or not.

Incorrect conversion of DepthToSpace in CRD mode

Issue Type

Others

onnx2tf version number

1.3.14

onnx version number

1.12.0

tensorflow version number

2.10.0

Download URL for ONNX

https://drive.google.com/file/d/1XeM7XpjOsL37qAdpWa1j6x9KMzYbiHgW/view?usp=sharing

Parameter Replacement JSON

{}

Description

  1. Personal development
  2. Pixelshuffle layer in PyTorch (CRD mode for ONNX) is not converting correctly to TF.
import torch
import tensorflow as tf
import numpy as np

from onnx2tf import convert

class PixelShuffle(torch.nn.Module):
    def __init__(self, upscale_factor=2):
        super().__init__()
        self._upscale_factor = upscale_factor
    def forward(self, inp):
        out = torch.nn.functional.pixel_shuffle(inp, upscale_factor=self._upscale_factor)
        return out

upscale_factor = 2
b, c, h, w = 2, 3, 8, 8
max_val = b*c*h*w*upscale_factor*upscale_factor
ps = PixelShuffle(upscale_factor=upscale_factor)
inp = (torch.arange(max_val)/max_val).view(b, c*upscale_factor*upscale_factor, h, w)
out_torch = ps(inp)

_ = torch.onnx.export(ps,
                      torch.randn_like(inp),
                      "ps.onnx",
                      opset_version=17,
                      export_params=True,
                      verbose=False,
                      input_names=["input"],
                      output_names=["output"])

convert(input_onnx_file_path="ps.onnx",
       output_folder_path="ps_tf",
       non_verbose=True)

interpreter = tf.lite.Interpreter(model_path="ps_tf/model_float32.tflite")
interpreter.allocate_tensors()

# Get input and output tensors
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

input_data = (inp).permute(0,2,3,1).cpu().numpy()

interpreter.set_tensor(input_details[0]['index'], input_data)

interpreter.invoke()

out_tf = interpreter.get_tensor(output_details[0]['index'])

print(np.allclose(out_torch.permute(0,2,3,1).numpy(), out_tf))

This gives False output.
3. I fixed the bug in the DepthToSpace op. Creating Pull Request now.

Avoid creating FlexTranspose for DepthToSpace

Issue Type

Feature Request

onnx2tf version number

1.3.14

onnx version number

1.12.0

tensorflow version number

2.10.0

Download URL for ONNX

https://drive.google.com/file/d/1XeM7XpjOsL37qAdpWa1j6x9KMzYbiHgW/view?usp=sharing

Parameter Replacement JSON

{}

Description

  1. Personal development
  2. Pixelshuffle layer in PyTorch (CRD mode for ONNX) produces FlexTranspose op when converting to TF.
    Screenshot 2023-01-04 121313
  3. I tried implementing a workaround as in #62, but that relies on squeezing dimensions of size 1. These are not necessarily present for PixelShuffle layers.
  4. The presence of FlexTranspose operations requires TFLite Flex delegates, which is not always possible when deploying.

[YoloX] output differs between onnx and tflite

Issue Type

Others

onnx2tf version number

1.4.2

onnx version number

1.12.0

tensorflow version number

2.10

Download URL for ONNX

https://drive.google.com/file/d/1QQLf83JwLI_EoQFU5xOGKUvtFPsFSWCN/view

Parameter Replacement JSON

N/A

Description

  1. Purpose: Personal development
  2. What: The outputs are different between onnx and converted tflite. the image below is tflite and onnx outputs consecutively.

  1. How: It looks line number 849-855 caused this error in common_functions.py. In this case, intermediate Sub layer returned wrong output because it didn't re-swapped the parameters. But I didn't fully understand why these lines are needed. What is the bug you mentioned in PR #99?

# Skip subsequent processing in the following patterns.
# const_or_var_1: [1,1,5000]
# const_or_var_2: [5000]
if len(const_or_var_1.shape) >= 1 \
and len(const_or_var_2.shape) == 1 \
and const_or_var_1.shape[-1] == const_or_var_2.shape[-1]:
return const_or_var_1, const_or_var_2

[MODNet] Dimensions must be equal, but are 15 and 1280 for '{{node tf.math.multiply_17/Mul}} = Mul[T=DT_FLOAT](Placeholder, Placeholder_1)' with input shapes: [1,15,20,1280], [1,1280,15,20]

Issue Type

Others

onnx2tf version number

1.1.46

Download URL for ONNX

https://github.com/DeepranjanG/Image_Background_Removal
https://github.com/DeepranjanG/Image_Background_Removal/raw/main/pretrained/modnet.onnx

Parameter Replacement JSON

{
  "format_version": 1,
  "operations": [
    {
      "op_name": "Expand_187",
      "param_target": "outputs",
      "param_name": "685",
      "post_process_transpose_perm": [0,2,3,1]
    }
  ]
}

Description

1.Personal development
2.Error occured.

onnx2tf -i modnet.onnx -ois "input:1,3,480,640" -prf replace.json
    Dimensions must be equal, but are 15 and 1280 for '{{node tf.math.multiply_17/Mul}} = Mul[T=DT_FLOAT](Placeholder, Placeholder_1)' with input shapes: [1,15,20,1280], [1,1280,15,20]

3.replace.json
5.[Experimental] Automatic transposition of input values (valid only if immediately preceding is "Conv") #36

[FastestDet] The absolute error of `AveragePool`, `GlobalAveragePool` is quite large

Issue Type

Others

onnx2tf version number

1.5.12

onnx version number

1.13.0

tensorflow version number

2.10.0

Download URL for ONNX

https://github.com/PINTO0309/onnx2tf/releases/download/1.1.28/fastestdet.onnx
https://github.com/PINTO0309/onnx2tf/releases/download/1.1.28/ppmattingv2_stdc1_human_480x640.onnx

Parameter Replacement JSON

N/A

Description

  1. Personal
  2. The absolute error of AveragePool is quite large. In addition, there is almost no error in other OPs.
    onnx2tf -i fastestdet.onnx -cotof -cotoa 1e-4
    
    image
    image
  3. At this time I do not know the cause of the larger error.
  4. I want to generate a tfite model with small accuracy degradation.

[japan_PP-OCRv3] For min_value and max_value in Clip.py

Issue Type

Others

onnx2tf version number

1.1.17

Download URL for ONNX

japan_PP-OCRv3_rec_infer.zip

Parameter Replacement JSON

None

Description

onnx2tf -i japan_PP-OCRv3_rec_infer.onnx

ERROR: The trace log is below.
Traceback (most recent call last):
  File "/home/systemkjapan/.local/lib/python3.8/site-packages/onnx2tf/utils/common_functions.py", line 261, in print_wrapper_func
    result = func(*args, **kwargs)
  File "/home/systemkjapan/.local/lib/python3.8/site-packages/onnx2tf/utils/common_functions.py", line 323, in inverted_operation_enable_disable_wrapper_func
    result = func(*args, **kwargs)
  File "/home/systemkjapan/.local/lib/python3.8/site-packages/onnx2tf/ops/Clip.py", line 109, in make_node
    if (min_value is not None and min_value.shape is not None) \
AttributeError: 'float' object has no attribute 'shape'

I don't think the code here will work if 'min_value' is 'float'?
Or is it wrong to be 'float'?

[ConvNext] Add layer shape wrong

Issue Type

Others

onnx2tf version number

1.1.24

Download URL for ONNX

https://drive.google.com/file/d/15TV0y-Go0VYdyV4FKNhciVDWa3frECfm/view?usp=sharing

Parameter Replacement JSON

{
  "format_version": 1,
  "operations": [
    {
      "op_name": "/encoder/stages.0/layers/layers.0/Transpose_1",
      "param_target": "outputs",
      "param_name": "/encoder/stages.0/layers/layers.0/Transpose_1_output_0",
      "post_process_transpose_perm": [0,2,3,1]
    }
  ]
}

Description

Hi - thanks for the super useful library.

I'm having trouble diagnosing the problem with an add op - I've narrowed it down to the op, but can't seem to get the right adjustment in the parameter_replacement.json file to fix it, and am not sure exactly how to spot the issue in the attached screenshots.

image

image

Many thanks!

When Onnx Matmul inputs have different dimension

Issue Type

Others

onnx2tf version number

1.5.18

onnx version number

1.13.0

tensorflow version number

2.10.0

Download URL for ONNX

test_model.zip

Parameter Replacement JSON

Not used

Description

  1. Purpose: Currently getting help from this library for research project of anomaly detection task. Thank you for building this library.
  2. What: Two inputs A, B of onnx Matmul ops are not equal. Error log is attached. error_log.txt
  3. How: I tried fixing it using param_replacement.json. But For Matmul ops, only axis transposing is suported, so I am not able to add dimension on the input.
    image
INFO:  input_name.1: input.35 shape: [1, 256, 66] dtype: float32
INFO:  input_name.2: onnx::MatMul_73 shape: [66, 2] dtype: <class 'numpy.float32'>

We are thinking if we can add one more dimension in input_name.2: onnx::MatMul_73 to [1, 66, 2], it can be resolved.

  1. Why: This onnx model is converted from torch module and using Autoencoder structure. It is important for us to keep current network structure. So we are hoping to be able to fix this dimension unequality issue.

Question about channel_transpose in common_functions.py

Issue Type

Others

onnx2tf version number

1.1.25

Download URL for ONNX

gist for reproduction : https://colab.research.google.com/gist/Hyunseok-Kim0/d0aaf6e9ac6fbe461c5f2364db4bc0b2/onnx2tf_20221117.ipynb

Parameter Replacement JSON

N/A

Description

  1. Purpose: Personal development
  2. What: channel_transpose in common_functions.py is used in arithmetic operations like Add, Sub, Mul, etc. What is the main purpose of this function? When second input has more dimension, channel_transpose adds additional squeeze layer and changes the output shape, not vice versa.
    Please see the gist (https://colab.research.google.com/gist/Hyunseok-Kim0/d0aaf6e9ac6fbe461c5f2364db4bc0b2/onnx2tf_20221117.ipynb). When the output of network is x = x + y, onnx and tflite has same output shape. Converted tflite has wrong shape for x = y + x.
ONNX Correct tflite (x = x + y) Wrong tflite (x = y + x)
Screenshot from 2022-11-17 11-07-34 Screenshot from 2022-11-17 11-26-23 Screenshot from 2022-11-17 11-11-00

Bug in TFlite NonMaxSuppression

Issue Type

Others

onnx2tf version number

1.2.9

Download URL for ONNX

Any onnx file with NonMaxSuppression can be used. Please use code below for simple debugging.

import os
import shutil

import onnxruntime
import tensorflow as tf
import torch
import torchvision
from torch import nn

from onnx2tf import convert


class dummy_network(nn.Module):

    def __init__(self):
        super(dummy_network, self).__init__()

    def forward(self, boxes, scores):
        # unique with sort
        result = torchvision.ops.nms(boxes, scores, iou_threshold=0.3)

        return result


def main():
    os.makedirs("tflite", exist_ok=True)
    model = dummy_network()
    model.eval()

    dummy_name = "dummy_nms"
    onnx_save_path = f"tflite/{dummy_name}.onnx"
    temp_tflite = "tflite/model_float32.tflite"
    tflite_save_path = f"tflite/{dummy_name}.tflite"

    dummy_input_boxes = torch.abs(torch.randn(32, 4, dtype=torch.float32))
    dummy_input_boxes = torch.concat([dummy_input_boxes, dummy_input_boxes], dim=0)
    dummy_scores = torch.abs(torch.randn(32, dtype=torch.float32))
    dummy_scores = torch.concat([dummy_scores, dummy_scores], dim=0)

    torch.onnx.export(model,
                      args=(dummy_input_boxes, dummy_scores),
                      f=onnx_save_path,
                      input_names=["boxes", "scores"],
                      output_names=["nms_result"],
                      opset_version=11)

    # get onnx output
    # -----------------------------------------------------------------------------------------------
    onnx_session = onnxruntime.InferenceSession(onnx_save_path, providers=['CPUExecutionProvider'])
    onnx_inputs = dict(boxes=dummy_input_boxes.numpy(), scores=dummy_scores.numpy())
    onnx_outputs = onnx_session.run(None, onnx_inputs)

    convert(onnx_save_path, output_folder_path="tflite")
    shutil.move(temp_tflite, tflite_save_path)

    # # compare torch output and onnx output
    # np.testing.assert_allclose(onnx_output, torch_output.numpy())
    #
    # get tflite output
    # -----------------------------------------------------------------------------------------------
    tflite_onnx2tf = tf.lite.Interpreter(model_path=tflite_save_path)
    tflite_onnx2tf.allocate_tensors()
    tflite_onnx2tf.set_tensor(tflite_onnx2tf.get_input_details()[0]['index'], dummy_input_boxes.numpy())
    tflite_onnx2tf.set_tensor(tflite_onnx2tf.get_input_details()[1]['index'], dummy_scores.numpy())
    tflite_onnx2tf.invoke()
    tflite_output = [tflite_onnx2tf.get_tensor(i['index']) for i in tflite_onnx2tf.get_output_details()][0]

    print("convert done")

    return


if __name__ == "__main__":
    main()

Parameter Replacement JSON

N/A

Description

  1. Purpose: Personal development

  2. What: onnx NonMaxSuppression operator is converted using tf.image.non_max_suppression. However, it looks there is bug in tensorflow operation.
    tensorflow/tensorflow#51629
    For now, tf.image.non_max_suppression always pad the number of output to max_output_boxes_per_class. The result below shows the difference between onnx and tflite output.
    Screenshot from 2022-12-15 15-02-08
    There is another operation, tf.image.non_max_suppression_padded which using extra parameter pad_to_max_output_size. I thought this problem can be solved by setting pad_to_max_output_size to False, but it also didn't work.
    Screenshot from 2022-12-15 15-01-28
    Returned indices are not correct, looks like the implementation is totally different between tf.image.non_max_suppression_padded and tf.image.non_max_suppression.

  3. How: This problem can be solved by adding single line of code selected_indices = tf.unique(selected_indices).y after line number 208. However, sometimes last element of indices can be wrong value since this solution is just removing duplicated indices.

    selected_indices = tf.image.non_max_suppression(
    tf_boxes,
    tf_scores,
    max_output_boxes_per_class,
    iou_threshold,
    score_threshold,
    )

[E2Pose] Convert error

Issue Type

Others

onnx2tf version number

1.2.2

Download URL for ONNX

https://github.com/AISIN-TRC/E2Pose/blob/main/pretrains/download.sh
https://github.com/PINTO0309/PINTO_model_zoo/tree/main/333_E2Pose

Parameter Replacement JSON

{
  "format_version": 1,
  "operations": [
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_decode_01_norm/FusedBatchNormV3__930",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_decode_02_norm/FusedBatchNormV3__1055",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_decode_03_norm/FusedBatchNormV3__1180",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_decode_04_norm/FusedBatchNormV3__1305",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_decode_02_p_seed/Conv2D__1065",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_decode_03_p_seed/Conv2D__1190",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_decode_04_p_seed/Conv2D__1315",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_decode_05_p_seed/Conv2D__1408",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_output_04_cyx_seed_split/split",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_output_03_cyx_seed_split/split",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_output_02_cyx_seed_split/split",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_output_01_cyx_seed_split/split",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_output_05_cyx_seed_split/split",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_output_04_cyx/concat",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_output_03_cyx/concat",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_output_02_cyx/concat",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_output_01_cyx/concat",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_output_05_cyx/concat",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    },
    {
      "op_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_output_04_cyx_reshape/Reshape",
      "param_target": "inputs",
      "param_name": "e2pose/E2Pose_Inference/E2Pose_pose_stage00_output_04_cyx/concat:0",
      "pre_process_transpose_perm": [0,2,3,1]
    }
  ]
}

Description

  1. Personal development
  2. Convert error occured
  3. replacement.json
  4. Because I want tflite.
  5. https://github.com/PINTO0309/onnx2tf/releases/tag/1.2.3

[TUNIT] conversion error, TypeError: 'Variable' object is not iterable

Issue Type

Others

onnx2tf version number

1.1.44

Download URL for ONNX

onnx/onnx-tensorflow#693
https://github.com/sayakpaul/Adventures-in-TensorFlow-Lite/releases/download/v0.2.0/tunit_onnx_files.tar.gz

Parameter Replacement JSON

{
  "format_version": 1,
  "operations": [
    {
      "op_name": "Reshape_136",
      "param_target": "outputs",
      "param_name": "194",
      "post_process_transpose_perm": [0,2,1]
    },
    {
      "op_name": "Reshape_143",
      "param_target": "outputs",
      "param_name": "205",
      "post_process_transpose_perm": [0,2,3,1]
    },
    {
      "op_name": "Reshape_170",
      "param_target": "outputs",
      "param_name": "234",
      "post_process_transpose_perm": [0,2,1]
    },
    {
      "op_name": "Reshape_177",
      "param_target": "outputs",
      "param_name": "245",
      "post_process_transpose_perm": [0,2,3,1]
    },
    {
      "op_name": "Reshape_206",
      "param_target": "outputs",
      "param_name": "276",
      "post_process_transpose_perm": [0,2,1]
    },
    {
      "op_name": "Reshape_213",
      "param_target": "outputs",
      "param_name": "287",
      "post_process_transpose_perm": [0,2,3,1]
    },
    {
      "op_name": "Reshape_240",
      "param_target": "outputs",
      "param_name": "316",
      "post_process_transpose_perm": [0,2,1]
    },
    {
      "op_name": "Reshape_247",
      "param_target": "outputs",
      "param_name": "327",
      "post_process_transpose_perm": [0,2,3,1]
    },
    {
      "op_name": "Reshape_303",
      "param_target": "outputs",
      "param_name": "385",
      "post_process_transpose_perm": [0,2,1]
    },
    {
      "op_name": "Reshape_310",
      "param_target": "outputs",
      "param_name": "396",
      "post_process_transpose_perm": [0,2,3,1]
    },
    {
      "op_name": "Reshape_364",
      "param_target": "outputs",
      "param_name": "452",
      "post_process_transpose_perm": [0,2,1]
    },
    {
      "op_name": "Reshape_371",
      "param_target": "outputs",
      "param_name": "463",
      "post_process_transpose_perm": [0,2,3,1]
    },
    {
      "op_name": "Reshape_425",
      "param_target": "outputs",
      "param_name": "519",
      "post_process_transpose_perm": [0,2,1]
    },
    {
      "op_name": "Reshape_432",
      "param_target": "outputs",
      "param_name": "530",
      "post_process_transpose_perm": [0,2,3,1]
    }
  ]
}

Description

1.Personal development
https://github.com/clovaai/tunit
image
2.TypeError: 'Variable' object is not iterable
3.replace.json
5.Error log

ERROR: The trace log is below.
Traceback (most recent call last):
  File "/home/xxxxx/git/onnx2tf/onnx2tf/utils/common_functions.py", line 262, in print_wrapper_func
    result = func(*args, **kwargs)
  File "/home/xxxxx/git/onnx2tf/onnx2tf/utils/common_functions.py", line 324, in inverted_operation_enable_disable_wrapper_func
    result = func(*args, **kwargs)
  File "/home/xxxxx/git/onnx2tf/onnx2tf/ops/BatchNormalization.py", line 41, in make_node
    graph_node.outputs = graph_node.outputs[0]
  File "/home/xxxxx/.local/lib/python3.8/site-packages/onnx_graphsurgeon/ir/node.py", line 89, in __setattr__
    getattr(self, name).extend(value)
  File "/home/xxxxx/.local/lib/python3.8/site-packages/onnx_graphsurgeon/util/misc.py", line 111, in extend
    super().extend(iterable)
TypeError: 'Variable' object is not iterable

[YOLOX-X] [Faster-RCNN] Error when outputting to h5 file. `AttributeError: 'numpy.dtype[int64]' object has no attribute 'item'`

Issue Type

Others

onnx2tf version number

1.5.28

onnx version number

1.13.0

tensorflow version number

2.10.0

Download URL for ONNX

https://s3.ap-northeast-2.wasabisys.com/temp-models/onnx2tf_146/yolox_x.onnx
https://github.com/PINTO0309/onnx2tf/releases/download/1.1.28/faster_rcnn-10.onnx

Parameter Replacement JSON

N/A

Description

  1. Persnal
  2. Error when outputting to Keras(h5) file
    onnx2tf -i yolox_x.onnx -oh5
    
    'numpy.dtype[int64]' object has no attribute 'item'
      File "/home/xxxxx/git/onnx2tf/onnx2tf/onnx2tf.py", line 744, in convert
        model.save(f'{output_folder_path}/{output_file_name}_float32.h5')
      File "/home/xxxxx/git/onnx2tf/onnx2tf/onnx2tf.py", line 1735, in main
        model = convert(
      File "/home/xxxxx/git/onnx2tf/onnx2tf/onnx2tf.py", line 1786, in <module>
        main()
    AttributeError: 'numpy.dtype[int64]' object has no attribute 'item'
    
    The problem does not occur in YOLOX-S. Thus, I see a problem on the part of Post-Process.
    https://s3.ap-northeast-2.wasabisys.com/temp-models/onnx2tf_146/yolox_s.onnx

[YOLOX] Abort onnxruntime when `keepdims=False` in `ReduceXX` and the final tensor dimension is reduced to zero

Issue Type

Others

onnx2tf version number

1.5.0

onnx version number

1.13.0

tensorflow version number

2.10.0

Download URL for ONNX

YOLOX-X
https://drive.google.com/file/d/1QQLf83JwLI_EoQFU5xOGKUvtFPsFSWCN/view

Parameter Replacement JSON

N/A

Description

Ref issue: [YoloX] output differs between onnx and tflite #107

Problems that can occur in most use cases, such as when post-processing results in zero cases. This is not an onnx2tf problem, but rather an onnxruntime specification problem.
image

ONNX and TF output value validation started =========================================
INFO: validation_conditions: np.allclose(onnx_outputs, tf_outputs, rtol=1e-05, atol=1e-05, equal_nan=True)
2023-01-10 14:18:07.353720422 [E:onnxruntime:, sequential_executor.cc:369 Execute] Non-zero status code returned while running ReduceMax node. Name:'ReduceMax_686' Status Message: /home/b920405/work/onnxruntime/onnxruntime/core/providers/cpu/reduction/reduction_ops.cc:763 void onnxruntime::ValidateKeepDims(const onnxruntime::TensorShape&, int64_t) keepdims was false. Can't reduce on dim with value of 0 if 'keepdims' is false. Invalid output shape would be produced. input_shape:{0,4}

[E] No function: __iter__ registered for opset: 11
Traceback (most recent call last):
  File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/b920405/.vscode/extensions/ms-python.python-2022.20.1/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 39, in <module>
    cli.main()
  File "/home/b920405/.vscode/extensions/ms-python.python-2022.20.1/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 430, in main
    run()
  File "/home/b920405/.vscode/extensions/ms-python.python-2022.20.1/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 284, in run_file
    runpy.run_path(target, run_name="__main__")
  File "/home/b920405/.vscode/extensions/ms-python.python-2022.20.1/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 321, in run_path
    return _run_module_code(code, init_globals, run_name,
  File "/home/b920405/.vscode/extensions/ms-python.python-2022.20.1/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 135, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/home/b920405/.vscode/extensions/ms-python.python-2022.20.1/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 124, in _run_code
    exec(code, run_globals)
  File "/home/b920405/git/onnx2tf/onnx2tf/onnx2tf.py", line 1521, in <module>
    main()
  File "/home/b920405/git/onnx2tf/onnx2tf/onnx2tf.py", line 1475, in main
    model = convert(
  File "/home/b920405/git/onnx2tf/onnx2tf/onnx2tf.py", line 974, in convert
    dummy_onnx_outputs: List[np.ndarray] = dummy_onnx_inference(
  File "/home/b920405/git/onnx2tf/onnx2tf/utils/common_functions.py", line 2832, in dummy_onnx_inference
    outputs = onnx_session.run(None, dummy_datas)
  File "/usr/local/lib/python3.8/dist-packages/onnxruntime/capi/onnxruntime_inference_collection.py", line 200, in run
    return self._sess.run(output_names, input_feed, run_options)
onnxruntime.capi.onnxruntime_pybind11_state.RuntimeException: [ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running ReduceMax node. Name:'ReduceMax_686' Status Message: /home/b920405/work/onnxruntime/onnxruntime/core/providers/cpu/reduction/reduction_ops.cc:763 void onnxruntime::ValidateKeepDims(const onnxruntime::TensorShape&, int64_t) keepdims was false. Can't reduce on dim with value of 0 if 'keepdims' is false. Invalid output shape would be produced. input_shape:{0,4}
  1. If keepdims=False, modify as follows to slice the corresponding dimension after performing ReduceXX with keepdims=True and keeping the dimension. https://github.com/PINTO0309/PINTO_model_zoo/blob/efe3e8da69e1ef14014430b1c88c790976ec10ef/342_ALIKE/PINTO_special/make_mnn_matcher.py#L23-L24
  2. Make the image np.ndarray be input as dummy data when --check_onnx_tf_outputs_elementwise_close is executed. sample: https://github.com/PINTO0309/onnx2tf/releases/download/1.0.49/calibration_image_sample_data_20x128x128x3_float32.npy

TypeError: EndVector() missing 1 required positional argument: 'vectorNumElems'

Issue Type

Others

onnx2tf version number

1.1.4

Download URL for ONNX

https://github.com/PINTO0309/onnx2tf/releases/download/1.1.3/lite-model_yamnet_classification_tflite_1.onnx

Parameter Replacement JSON

{
  "format_version": 1,
  "operations": [
    {
      "op_name": "tower0/network/layer1/conv/Relu6;tower0/network/layer1/conv/BatchNorm/FusedBatchNormV3;tower0/network/layer2/sepconv/BatchNorm/FusedBatchNormV3;tower0/network/layer2/sepconv/depthwise;tower0/network/layer1/conv/Conv2D_prequant__219",
      "param_target": "inputs",
      "param_name": "new_shape__513",
      "values": [1,96,64,1]
    }
  ]
}

Description

  1. Error when calling tflite converter.
    onnx2tf -i lite-model_yamnet_classification_tflite_1.onnx -prf replace.json
    TypeError: EndVector() missing 1 required positional argument: 'vectorNumElems'
  2. I upgraded flatbuffers by referring to the following.
    https://stackoverflow.com/questions/73442005/tflite-model-maker-in-colab-typeerror-endvector-missing-1-required-positional
    From:
      pip show flatbuffers
      Name: flatbuffers
      Version: 1.12
    
    To:
      pip install -U flatbuffers
    
      pip show flatbuffers
      Name: flatbuffers
      Version: 22.10.26
    
  3. Cannot convert to tflite.
  4. Solution
    pip install -U flatbuffers
    

Bug in `onnx_tf_tensor_validation`

Issue Type

Others

onnx2tf version number

1.5.23

onnx version number

1.12.0

tensorflow version number

2.10

Download URL for ONNX

https://github.com/PINTO0309/onnx2tf/releases/download/1.1.28/pidnet_S_cityscapes_192x320.onnx

Parameter Replacement JSON

N/A

Description

  1. Purpose: Personal development
  2. What: onnx_tf_tensor_validation use tensor shape to find out matching pairs between onnx and tflite outputs. Due to this logic, some outputs are failed to matched and generates wrong absolute error.

The output of global average pooling has shape of (1 x 512 x 1 x 1), and another average pooling output also have same shape and same value. When onnx_tf_tensor_validation is running, the output of global average pooling is failed to be matched with correct onnx output, generates Unmatched result as below.

Screenshot from 2023-01-20 17-06-40

Similar bug can be happen in other onnx files if some outputs have same shape and same value.

Problem with the docker image

Issue Type

Others

onnx2tf version number

1.1.38

Download URL for ONNX

none

Parameter Replacement JSON

none

Description

  1. Personal dev
  2. While trying to run the docker image with docker run --rm -it -v `pwd`:/workdir -w /workdir ghcr.io/pinto0309/onnx2tf:1.1.38 I get the following error:
Unable to find image 'ghcr.io/pinto0309/onnx2tf:1.1.38' locally
docker: Error response from daemon: Head "https://ghcr.io/v2/pinto0309/onnx2tf/manifests/1.1.38": unauthorized.
  1. I'm considering building it myself
  2. I just would like the documentation to be correct
  3. None

Also I wonder what would be the best way to then go from tensorflow to tflite?

[ALIKE] Inference error for `MaxPool` with padding is very large

Issue Type

Others

onnx2tf version number

1.5.33

onnx version number

1.13.1

tensorflow version number

2.10.0

Download URL for ONNX

https://github.com/PINTO0309/onnx2tf/releases/download/1.1.28/alike_t_opset11_192x320.onnx

Parameter Replacement JSON

N/A

Description

  1. Personal
  2. Inference error for MaxPool with padding is very large. However, I believe a separate investigation is needed to determine if this is a problem with the logic used to verify accuracy or with MaxPool's padding process.
    https://github.com/PINTO0309/onnx2tf/releases/download/1.1.28/alike_t_opset11_192x320.onnx
    image
    image

[human_segmentation_pphumanseg_2021oct] `-cotof` option is used, Validation of 1D tensor always results in `Unmatched`

Issue Type

Others

onnx2tf version number

1.5.8

onnx version number

1.13.0

tensorflow version number

2.10.0

Download URL for ONNX

https://github.com/PINTO0309/onnx2tf/releases/download/1.1.27/human_segmentation_pphumanseg_2021oct.onnx

Parameter Replacement JSON

{
  "format_version": 1,
  "operations": [
    {
      "op_name": "Resize_0",
      "param_target": "inputs",
      "param_name": "Concat_0",
      "values": [48,48]
    },
    {
      "op_name": "Resize_1",
      "param_target": "inputs",
      "param_name": "Concat_1",
      "values": [48,48]
    },
    {
      "op_name": "Resize_2",
      "param_target": "inputs",
      "param_name": "Concat_2",
      "values": [48,48]
    },
    {
      "op_name": "Resize_3",
      "param_target": "inputs",
      "param_name": "Concat_3",
      "values": [24,24]
    },
    {
      "op_name": "Resize_4",
      "param_target": "inputs",
      "param_name": "Concat_4",
      "values": [48,48]
    },
    {
      "op_name": "Resize_5",
      "param_target": "inputs",
      "param_name": "Concat_5",
      "values": [48,48]
    },
    {
      "op_name": "Resize_6",
      "param_target": "inputs",
      "param_name": "Concat_6",
      "values": [48,48]
    },
    {
      "op_name": "Resize_7",
      "param_target": "inputs",
      "param_name": "Concat_7",
      "values": [24,24]
    },
    {
      "op_name": "Resize_8",
      "param_target": "inputs",
      "param_name": "Concat_8",
      "values": [24,24]
    },
    {
      "op_name": "Resize_9",
      "param_target": "inputs",
      "param_name": "Concat_9",
      "values": [12,12]
    },
    {
      "op_name": "Resize_10",
      "param_target": "inputs",
      "param_name": "Concat_10",
      "values": [48,48]
    },
    {
      "op_name": "Resize_11",
      "param_target": "inputs",
      "param_name": "Concat_11",
      "values": [48,48]
    },
    {
      "op_name": "Resize_12",
      "param_target": "inputs",
      "param_name": "Concat_12",
      "values": [48,48]
    },
    {
      "op_name": "Resize_13",
      "param_target": "inputs",
      "param_name": "Concat_14",
      "values": [192,192]
    },
    {
      "op_name": "Reshape_0",
      "param_target": "outputs",
      "param_name": "Reshape_0",
      "post_process_transpose_perm": [0,2,3,1]
    },
    {
      "op_name": "Reshape_1",
      "param_target": "outputs",
      "param_name": "Reshape_1",
      "post_process_transpose_perm": [0,2,3,1]
    },
    {
      "op_name": "Transpose_0",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "Transpose_1",
      "param_target": "attributes",
      "param_name": "perm",
      "values": [0,1,2,3]
    },
    {
      "op_name": "Softmax_0",
      "param_target": "attributes",
      "param_name": "axis",
      "values": 3
    }
  ]
}

Description

Validation of 1D tensor always results in Unmatch. However, no inference error is introduced. Therefore, there is no problem with the behavior of the model, but only with the accuracy check decision.

ONNX and TF output value validation started =========================================
INFO: validation_conditions: np.allclose(onnx_outputs, tf_outputs, rtol=0.0, atol=1.0, equal_nan=True)
INFO: onnx_output_name: Shape_20 shape: (4,) dtype: int64 validate_result:  Unmatched 
INFO: onnx_output_name: conv2d_73.tmp_0 shape: (1, 64, 96, 96) dtype: float32 validate_result:  Matches 
INFO: onnx_output_name: shape_10.tmp_0 shape: (4,) dtype: int32 validate_result:  Unmatched 
INFO: onnx_output_name: batch_norm_0.tmp_2 shape: (1, 64, 96, 96) dtype: float32 validate_result:  Matches 
INFO: onnx_output_name: shape_10.tmp_0_slice_0 shape: (2,) dtype: int32 validate_result:  Unmatched 
INFO: onnx_output_name: relu_0.tmp_0 shape: (1, 64, 96, 96) dtype: float32 validate_result:  Matches 
INFO: onnx_output_name: Cast_21 shape: (2,) dtype: int64 validate_result:  Unmatched 
INFO: onnx_output_name: conv2d_74.tmp_0 shape: (1, 64, 48, 48) dtype: float32 validate_result:  Matches 
INFO: onnx_output_name: batch_norm_1.tmp_2 shape: (1, 64, 48, 48) dtype: float32 validate_result:  Matches 
INFO: onnx_output_name: relu_1.tmp_0 shape: (1, 64, 48, 48) dtype: float32 validate_result:  Matches 
INFO: onnx_output_name: conv2d_75.tmp_0 shape: (1, 32, 48, 48) dtype: float32 validate_result:  Matches 
INFO: onnx_output_name: conv2d_78.tmp_0 shape: (1, 128, 48, 48) dtype: float32 validate_result:  Matches 

image

Batchnorm after convtranspose converted to wrong bias

Issue Type

Others

onnx2tf version number

1.1.23

Download URL for ONNX

gist for reproduction : https://colab.research.google.com/gist/Hyunseok-Kim0/41920f44e994caa96271d604e5831775/onnx2tf_batchnorm_test.ipynb

Parameter Replacement JSON

N/A

Description

First of all, thank you for sharing another wonderful tool. onnx2tf looks very stable and useful.

  1. Purpose: Personal development

  2. What : I tried to convert centernet (https://github.com/xingyizhou/CenterNet) to tflite. It was very impressive that initial conversion from onnx to tflite was completed without an error using onnx2tf. However, I found that generated tflite using onnx2tf shows different output comparing with the one generated from openvino2tensorflow. Most of network components were identical except the TransposeConv layer, which was followed by Batchnorm layer.

Screenshot from 2022-11-16 16-24-20
part of tflite from openvino2tensorflow (generates correct output)

Screenshot from 2022-11-16 16-24-39-1
part of tflite from onnx2tf (generates wrong output)

As you see in the figures above, the TransposeConv layer from openvino2tensorflow has no bias while onnx2tf version has some bias. Also, the Add layers which is converted from Batchnorm layer has different values which are shown in the right side of images.

I think this problem is related with Batchnorm layer, but couldn't find solution.

  1. How: please use the gist to reproduce this problem. (https://colab.research.google.com/gist/Hyunseok-Kim0/41920f44e994caa96271d604e5831775/onnx2tf_batchnorm_test.ipynb)
    The gist generates dummy pytorch network which contains only two layers, (Conv + Batchnorm) or (ConvTranspose + Batchnorm). After the conversion to tflite, the inference output is compared each other.
    For the network with (Conv + Batchnorm), it shows same network structure and same outputs. However, (ConvTranspose + Batchnorm) shows different outputs.

Screenshot from 2022-11-16 17-01-22
target onnx

Screenshot from 2022-11-16 17-01-03
tflite from openvino2tensorflow

Screenshot from 2022-11-16 17-01-13
tflite from onnx2tf

In tflite from onnx2tf, bias of Batchnrom layer is moved to Add layer, and another unknown bias value is added to TransposeConv layer. In tflite from openvino2tensorflow, TransposeConv layer has no bias and unknown value is added in Add layer.

Also, I found there is typo in ConvTranspose.py in line 68. tf_layers_dict[input_weights.name] instead of tf_layers_dict[input_bias.name], but it does not look like cause of this problem..

input_tensor = tf_layers_dict[input_tensor.name]['tf_node'] \
if isinstance(input_tensor, gs.Variable) else input_tensor
input_tensor_shape = input_tensor.shape
input_weights = tf_layers_dict[input_weights.name]['tf_node'] \
if isinstance(input_weights, gs.Variable) else input_weights
input_bias = tf_layers_dict[input_weights.name]['tf_node'] \
if isinstance(input_bias, gs.Variable) else input_bias

I will keep try to find out the solution, please share your idea about this problem.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.