TensorFlow 2 Exporting to ONNX and Model Visualization Tutorial

1 Background

After training a network model using a deep learning open source framework, a format conversion is usually required before deployment, and the Horizon Toolchain model conversion currently supports Caffe1.0 and ONNX(opset_version=10/11 and ir_version≤7). ONNX(Open Neural Network Exchange) format is a common open source neural network format, supported by many inference engines, such as Pytorch, PaddlePaddle, TensorFlow, etc. This article describes in detail how to export the resulting model from TensorFlow2 to ONNX format.

2 Experimental environment

The experimental environment for this tutorial is as follows:

Python library

Version

tensorflow-cpu

2.11.0

tensorflow-intel

2.11.0

tf2onnx

1.13.0

protobuf

3.20.2

onnx

1.13.0

onnxruntime

1.14.0

3 tf2onnx tool introduction

tf2onnx can convert the TensorFlow/Keras model to ONNX through the command line. The main configuration parameters of the tool are as follows:

python -m tf2onnx.convert
    --saved-model   
    --output  
    --opset    
    --inputs
    --outputs

tf2onnx: https://github.com/onnx/tensorflow-onnx

4 codes

TensorFlow2ONNX。

import tensorflow as tf
import os
import onnx

def MyNet():
    input1 = tf.keras.layers.Input(shape=(7, 7, 3))

    x = tf.keras.layers.Conv2D(16, (3, 3),
               activation='relu',
               padding='same',
               name='conv1')(input1)
    x = tf.keras.layers.Conv2D(16, (3, 3),
               activation='relu',
               padding='same',
               name='conv2')(x)

    x = tf.keras.layers.Flatten(name='flatten')(x)
    x = tf.keras.layers.Dense(100, activation='relu', name='fc1')(x)
    output = tf.keras.layers.Dense(2, activation='softmax', name='predictions')(x)

    input_1 = input1
    model = tf.keras.models.Model(inputs=[input_1], outputs=output)
    return model

model = MyNet()

model.save('model')
os.system("python -m tf2onnx.convert --saved-model model --output model.onnx --opset 11")

ONNX correctness verification

You can verify the correctness of the ONNX model with the following code, which checks the version of the model, the structure of the graph, the nodes, and the input and output. If the output is Check: None, no error information is reported and the model is correctly exported.

import onnx

onnx_model = onnx.load("./model.onnx")
check = onnx.checker.check_model(onnx_model)
print('Check: ', check)

Consistency check between TensorFlow2 and ONNX You can use the following code to check that the derived ONNX model and the original PaddlePaddle model have the same calculations.

import tensorflow as tf
import onnxruntime
import numpy as np

input1 = np.random.random((1, 7, 7, 3)).astype('float32')

ort_sess = onnxruntime.InferenceSession("./model.onnx")
ort_inputs = {ort_sess.get_inputs()[0].name: input1}
ort_outs = ort_sess.run(None, ort_inputs)

tf_model = tf.saved_model.load(export_dir="model")
tf_outs = tf_model(inputs=input1)

print(ort_outs[0])
print(tf_outs.numpy())
np.testing.assert_allclose(tf_outs.numpy(), ort_outs[0], rtol=1e-03, atol=1e-05)
print("onnx model check finsh.")

Case of multiple inputs

If your model has multiple inputs, you can refer to the code below to save TensorFlow2 in save-model mode and convert to ONNX format.

import tensorflow as tf
import os

def MyNet():
    input1 = tf.keras.layers.Input(shape=(7, 7, 3))
    input2 = tf.keras.layers.Input(shape=(7, 7, 3))

    x = tf.keras.layers.Conv2D(16, (3, 3),
               activation='relu',
               padding='same',
               name='conv1')(input1)
    y = tf.keras.layers.Conv2D(16, (3, 3),
               activation='relu',
               padding='same',
               name='conv2')(input2)
    z = tf.keras.layers.Concatenate(axis=-1)([x, y])
    z = tf.keras.layers.Flatten(name='flatten')(z)
    z = tf.keras.layers.Dense(100, activation='relu', name='fc1')(z)
    output = tf.keras.layers.Dense(2, activation='softmax', name='predictions')(z)

    input_1 = input1
    input_2 = input2
    model = tf.keras.models.Model(inputs=[input_1,input_2], outputs=output)
    return model

model = MyNet()

model.save('model')
os.system("python -m tf2onnx.convert --saved-model model --output model.onnx --opset 11")

Set input/output nodes

Sometimes, due to the difficulty of deployment, we do not want the pre - and post-processing parts of the TensorFlow network structure to be imported into the ONNX model. At this point, we can use the inputs and outputs parameters of the tf2onnx tool to specify the first and last exported nodes, so that nothing before and after the first node is imported into the ONNX model.

5 ONNX model visualization

Once exported into the ONNX model, you can use the open source visualization tool Netron to view the network structure and related configuration information. Netron use mainly divided into two kinds, one kind is to use the online web page version of https://netron.app/, another kind is to download the installation program at https://github.com/lutzroeder/netron. The visualizations of the model in this tutorial are:

6 ir_version and opset_version are modified

The ONNX model supported by Horizon Toolchain must meet opset_version=10/11 and ir_version≤7. When the ONNX model obtained does not meet these two requirements, you can modify the code and re-export it, or try to write scripts to directly modify the corresponding attributes of the ONNX model. Example code for the second approach is as follows:

import onnx

model = onnx.load("./model.onnx")
model.ir_version = 6
model.opset_import[0].version = 11
onnx.save_model(model, "./model_version.onnx")

** Note: ** There may be problems when switching from the high version to the low version, here is only one solution to try.

7 ONNX I/O dimension changed

When it is found that the input and output nodes of the ONNX model saved by the tf2onnx tool have abnormal values, such as the following situations: We can edit it with codes below.

import onnx

onnx_model = onnx.load("./model.onnx")
onnx_model.graph.input[0].type.tensor_type.shape.dim[0].dim_value = 1
onnx_model.graph.output[0].type.tensor_type.shape.dim[0].dim_value = 1
onnx.save(onnx_model, './model_dim.onnx')

Open the saved ONNX model file and you can see that the dimensions of the input and output nodes are normal: At this point, the ONNX model has satisfied the transformation condition of the Horizon toolchain.