My Keras yolov3-tiny.h5 model has an input tensor with dimensions ?,?,?,3 while I would expect ?,416,416,3 - tensorflow

When inspecting the Keras model yolov3-tiny.h5 using netron
I see that the input node is called input_1 and has type float32[?,?,?,3]. I would expect float32[?,416,416,3]
How can I force it to be float32[?,416,416,3]?
This is needed for downstream processing. The Keras model has to be converted to a frozen_model.pb in Tensorflow and then be further processed for deployement.
The deployement tools cannot handle an input with unknow w,h size.
Here is how I generated the Keras model. I downloaded the yolov3-tiny.cfg (https://github.com/pjreddie/darknet/blob/master/cfg/yolov3-tiny.cfg) and yolov3-tiny.weigths (https://pjreddie.com/media/files/yolov3-tiny.weights)
And then converted the model to a keras model using the following command :
python convert.py -p yolov3-tiny.cfg yolov3-tiny.weights model_data/yolov3-tiny.h5
(this code is obtained by cloning https://github.com/qqwweee/keras-yolo3)
Making a prediction using the saved Keras model works fine :
python yolo_video.py --image --model model_data/yolov3-tiny.h5
However when inspecting the Keras model yolov3-tiny.h5 using netron
I see that the input node is called input_1 and has type float32[?,?,?,3]
I would expect float32[?,416,416,3]
How can I force it to be float32[?,416,416,3]?

You could do that while converting the keras model file to tflite by passing input_shapes as a dictionary with input node name as the key and shape as the value.
Check out the TFLiteConverter class here
def _main_(args):
kmodel = args.kmodel
lmodel = args.lmodel
converter = tf.lite.TFLiteConverter.from_keras_model_file(kmodel, input_shapes={"input_1":[1,416,416,3]})
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
open("lmodel", "wb").write(tflite_model)
if __name__ == '__main__':
argparser = argparse.ArgumentParser(description='Quantize a trained yolo model')
argparser.add_argument('-k', '--kmodel', help='path to model weights file input')
argparser.add_argument('-l', '--lmodel', help='path to tensor flow lite output')
args = argparser.parse_args()
_main_(args)

Related

Tensorflow 2 SSD MobileNet model breaks during conversion to tflite

I've been trying to follow this process to run an object detector (SSD MobileNet) on the Google Coral Edge TPU:
Edge TPU model workflow
I've successfully trained and evaluated my model with the Object Detection API. I have the model both in checkpoint format as well as tf SavedModel format. As per the documentation, the next step is to convert to .tflite format using post-training quantization.
I am to attempting to follow this example. The export_tflite_graph_tf2.py script and the conversion code that comes after run without errors, but I see some weird behavior when I try to actually use the model to run inference.
I am unable to use the saved_model generated by export_tflite_graph_tf2.py. When running the following code, I get an error:
print('loading model...')
model = tf.saved_model.load(tflite_base)
print('model loaded!')
results = model(image_np)
TypeError: '_UserObject' object is not callable --> results = model(image_np)
As a result, I have no way to tell if the script broke my model or not before I even convert it to tflite. Why would model not be callable in this way? I have even verified that the type returned by tf.saved_model.load() is the same when I pass in a saved_model before it went through the export_tflite_graph_tf2.py script and after. The only possible explanation I can think of is that the script alters the object in some way that causes it to break.
I convert to tflite with post-training quantization with the following code:
def representative_data_gen():
dataset_list = tf.data.Dataset.list_files(images_dir + '/*')
for i in range(100):
image = next(iter(dataset_list))
image = tf.io.read_file(image)
# supports PNG as well
image = tf.io.decode_image(image, channels=3)
image = tf.image.resize(image, [IMAGE_SIZE, IMAGE_SIZE])
image = tf.cast(image / 255., tf.float32)
image = tf.expand_dims(image, 0)
if i == 0:
print(image.dtype)
yield [image]
# This enables quantization
# This sets the representative dataset for quantization
converter = tf.lite.TFLiteConverter.from_saved_model(base_saved_model)
# converter = tf.lite.TFLiteConverter.from_keras(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT] # issue here?
converter.representative_dataset = representative_data_gen
converter.target_spec.supported_ops = [
# tf.lite.OpsSet.TFLITE_BUILTINS, # enable TensorFlow Lite ops.
# tf.lite.OpsSet.SELECT_TF_OPS, # enable TensorFlow ops.
tf.lite.OpsSet.TFLITE_BUILTINS_INT8 # This ensures that if any ops can't be quantized, the converter throws an error
]
# This ensures that if any ops can't be quantized, the converter throws an error
# For full integer quantization, though supported types defaults to int8 only, we explicitly declare it for clarity.
converter.target_spec.supported_types = [tf.int8]
converter.target_spec.supported_ops += [tf.lite.OpsSet.TFLITE_BUILTINS]
# These set the input and output tensors to uint8 (added in r2.3)
converter.inference_input_type = tf.uint8
converter.inference_output_type = tf.uint8
tflite_model_quantized = converter.convert()
Everything runs with no errors, but when I try to actually run an image through the model, it returns garbage. I tried removing the quantization to see if that was the issue, but even without quantization it returns seemingly random bounding boxes that are completely off from the model's performance prior to conversion. The shape of the output tensors look fine, it's just the content is all wrong.
What's the right way to get this model converted to a quantized tflite form? I should note that I can't use the tflite_convert utility because I need to quantize the model, and it appears according to the source code that the quantize_weights flag is deprecated? There are a bunch of conflicting resources I see from TF1 and TF2 about this conversion process so I'm pretty confused.
Note: I'm using a retrained SSD MobileNet from the model zoo. I have not made any changes to the architecture in my training workflow. I've confirmed that the errors persist even on the base model pulled directly from the object detection model zoo.
I’m have a very similar problem with Post Training Quantization and asked about it on GitHub
I could manage to get results from the TFLite model but they were not good enough. Here is the notebook how I did it. Maybe it helps you to get a step forward.

Unable to convert .h5 model to ONNX for inferencing through any means

I built a custom model in .h5 from Matterport's MaskRCNN implementation. I managed to save the full model and not the weights alone using model.keras_model.save(), and assume it worked correctly.
I need to convert this model to ONNX to inference in Unity Barracuda, and I have been hitting several errors along the way.
I tried:
T1. .h5 to ONNX using this tutorial and the keras2onnx package, and I hit an error at:
model = load_model('model.h5')
Error:
ValueError: Unknown layer: BatchNorm
T2. Defining custom layers using this GitHub code:
model = keras.models.load_model(r'model.h5', custom_objects={'BatchNorm':BatchNorm,
'tf':tf, 'ProposalLayer':ProposalLayer,
'PyramidROIAlign1':PyramidROIAlign1, 'PyramidROIAlign2':PyramidROIAlign2,
'DetectionLayer':DetectionLayer}, compile=False)
Error:
ValueError: No model found in config file.
ValueError: Unknown layer: PyramidROIAlign
T3. .h5 to .pb (frozen graph) and .pbtxt, and then from .pb to ONNX using tf2onnx after finding input and output nodes (seems to be only one of each?):
assert d in name_to_node, "%s is not in graph" % d
AssertionError: output0 is not in graph
T4. .h5 to SavedModel using tf-serving code from here and then python -m tf2onnx.convert --saved-model exported_models\coco_mrcnn\3 --opset 15 --output "model.onnx" to convert to ONNX:
ValueError: make_sure failure: variable mrcnn_detection/map/while/Enter already exists as state variable.
TLDR: Is there a way to convert my .h5 model to ONNX through any direct/indirect means? I have been stuck on this for days!
Thanks in advance.
Edit 1:
It seems that keras.models.load_model() throws the first two errors - wondering if there is a way I can work with the .pb/.pbtxt model, or a way around without using load_model(), or a way to solve the load_model() issue?
Edit 2:
Code for T1:
custom dataset modified from Matterport's MaskRCNN implementation
Code for T4
Try converting it to saved model format and then to onnx.
import numpy as np
import tensorflow as tf
from tensorflow import keras
def get_model():
# Create a simple model.
inputs = keras.Input(shape=(32,))
outputs = keras.layers.Dense(1)(inputs)
model = keras.Model(inputs, outputs)
model.compile(optimizer="adam", loss="mean_squared_error")
return model
model = get_model()
# Train the model.
test_input = np.random.random((128, 32))
test_target = np.random.random((128, 1))
model.fit(test_input, test_target)
# Calling `save('my_model.h5')` creates a h5 file `my_model.h5`.
model.save("my_h5_model.h5")
# It can be used to reconstruct the model identically.
model = keras.models.load_model("my_h5_model.h5")
tf.saved_model.save(model, "tmp_model")
Then convert it using tf2onnx.
python3 -m tf2onnx.convert --saved-model tmp_model --output "model.onnx"
this works for me
via anaconda powershell console (execute as admin) :
pip install tf2onnx
pip install onnxmltools
and in a notebook (for example)
from tensorflow.python.keras.models import load_model
import os
os.environ['TF_KERAS'] = '1'
import onnxmltools
model = load_model('[h5 path]')
onnx_model = onnxmltools.convert_keras(model)
onnxmltools.utils.save_model(onnx_model, '[onnx path]')

Custom object detection model to TensorFlow Lite, shape of model input

I need to export a custom object detection model, fine-tuned on a custom dataset, to TensorFlow Lite, so that it can run on Android devices.
I'm using TensorFlow 2.4.1 on Ubuntu 18.04, and so far this is what I did:
fine-tuned an 'ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8' model, using a dataset of new images. I used the 'model_main_tf2.py' script from the repository;
I exported the model using 'exporter_main_v2.py'
python exporter_main_v2.py --input_type image_tensor --pipeline_config_path .\models\custom_model\pipeline.config --trained_checkpoint_dir .\models\custom_model\ --output_directory .\exported-models\custom_model
which produced a Saved Model (.pb file);
3. I tested the exported model for inference, and everything works fine. In the detection routine, I used:
def get_model_detection_function(model):
##Get a tf.function for detection
#tf.function
def detect_fn(image):
"""Detect objects in image."""
image, shapes = model.preprocess(image)
prediction_dict = model.predict(image, shapes)
detections = model.postprocess(prediction_dict, shapes)
return detections, prediction_dict, tf.reshape(shapes, [-1])
return detect_fn
and the shape of the produced image object is 640x640, as expected.
Then, I tried to convert this .pb model to tflite.
After updating to the nightly version of tensorflow (with the normal version, I got an error), I was actually able to produce a .tflite file by using this code:
import tensorflow as tf
from tflite_support import metadata as _metadata
saved_model_dir = 'exported-models/custom_model/'
## Convert the model
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.experimental_new_converter = True
converter.target_spec.supported_ops = [
tf.lite.OpsSet.TFLITE_BUILTINS, tf.lite.OpsSet.SELECT_TF_OPS]
tflite_model = converter.convert()
# Save the model.
with open('tflite/custom_model.tflite', 'wb') as f:
f.write(tflite_model)
I tried to use this model in AndroidStudio, following the instructions given here.
However, I'm getting a couple of errors:
something regarding 'Not a valid Tensorflow lite model' (have to check better on this);
the error:
java.lang.IllegalArgumentException: Cannot copy to a TensorFlowLite tensor (serving_default_input_tensor:0) with 3 bytes from a Java Buffer with 270000 bytes.
The second error seems to indicate there's something weird with the input expected from the tflite model.
I examined the file with Netron, and this is what I got:
the input is expected to have...1x1x1x3 shape, or am I misinterpreting the graph?
Should I somehow set the tensor input size when using the tflite exporter?
Anyway, what is the right way to export my custom model so that it can run on Android?
TF Ops are supported via the Flex delegate. I bet that is the problem. If you want to check if it is that, you can do:
Download benchmark app with flex delegate support for TF Ops. You can find it here, in the section Native benchmark binary: https://www.tensorflow.org/lite/performance/measurement. For example, for android is https://storage.googleapis.com/tensorflow-nightly-public/prod/tensorflow/release/lite/tools/nightly/latest/android_aarch64_benchmark_model_plus_flex
Connect your phone to your computer and where you have downloaded the apk, do adb push <apk_name> /data/local/tmp
Push your model adb push <tflite_model> /data/local/tmp
Open shell adb shell and go to folder cd /data/local/tmp. Then run the app with ./<apk_name> --graph=<tflite_model>
Info from:
https://www.tensorflow.org/lite/guide/ops_select
https://www.tensorflow.org/lite/performance/measurement

Unable to load the frozen model (.pb) in GraphDef in tensorflow version 2.x

Created a simple dummy sequential model in tf.keras as shown below:
model = tf.keras.Sequential()
model.add(layers.Dense(10, input_shape=(100, 100)))
model.add(layers.Conv1D(3, 2))
model.add(layers.Flatten())
model.add(layers.Dense(10, activation='softmax', name='predict_10'))
Trained the model and saved it using tf.keras.models.saved_model.
To get the input input and output node names used saved_model_cli.
saved_model_cli show --dir "path/to/SavedModel" --all
Froze the saved model with freeze_graph.py utility.
python freeze_graph.py --input_saved_model_dir=<path/to/SavedModel> --output_graph=<path/freeze.pb> --input_binary=True --output_node_names=StatefulPartitionedCall
Model is frozen.
Now Here's the main issue:
To load the frozen graph I've used this guide Migrate tf1.x to tf2.x (wrap_frozen_graph)
Used
with tf.io.gfile.GFile("patf/to/freeze.pb", 'rb') as f:
graph_def = tf.compat.v1.GraphDef()
graph_def.ParseFromString(f.read())
load_frozen = wrap_frozen_graph(graph_def, inputs='dense_3_input:0', outputs='predict_10:0')
Output error
ValueError: Input 1 of node StatefulPartitionedCall was passed float from dense_3/kernel:0 incompatible with expected resource.
I'm getting same error when converting .pb to .dlc (Qualcomm).
Actually I want to run original model on Qualcomm's Hexagon DSP or GPU.

Tensorflow - h5 model to tflite conversion error

I've made a learning transfer using a pre-trained InceptionV3 model, and I saved the h5 model file. After that, I am able to make predictions.
Now, I want to convert the h5 model to tflite file, using TFLiteConverter.convert() method, like this:
converter = lite.TFLiteConverter.from_keras_model_file('keras.model.h5')
tflite_model = converter.convert()
but I get this error:
File "from_saved_model.py", line 28, in <module>
tflite_model = converter.convert()
File "C:\Anaconda3\lib\site-packages\tensorflow\contrib\lite\python\lite.py", line 409, in convert
"invalid shape '{1}'.".format(_tensor_name(tensor), shape))
ValueError: None is only supported in the 1st dimension. Tensor 'input_1' has invalid shape '[None, None, None, 3]'
I am running Anaconda Python 3.6.8 on Windows 10 64 bits. Thank you in advance for your help!
Only the batch size (index 0) is allowed to be None when converting the model from TensorFlow to TensorFlow Lite. You should be able to use the input_shapes argument when calling from_keras_model_file to get the input array shape to be valid. For an InceptionV3 model, the input_shapes argument is often {'Mul' : [1,299,299,3]}.
The documentation for TFLiteConverter.from_keras_model_file is available here. The accepted parameters are as follows (copied from the documentation):
from_keras_model_file(
cls,
model_file,
input_arrays=None,
input_shapes=None,
output_arrays=None
)
load the keras.model.h5
set the input_shape, just avoid [None, None, None, 3]
save it as a new model.
Convert it just using the code you post in the question.
The batch_size is the only dimension that can be given as none.
The first dimension in the input_shape is the batch_size, the second and third dimensions indicate the input size of the image while the last one indicates the number of channels (RGB).
To avoid the error you get, specify the dimensions beforehand.
This can be achieved using toco (a tool which directly converts the acquired keras model into .tflite without converting it first to a .pb model and then to a .tflite model).
Using input_shape argument in toco you can specify the dimensions of the input_shape of your keras model.
Install toco for python and then run the following command,
toco --output_file = output_model.tflite --keras_model_file = keras.model.h5 --input_arrays input_1 --input_shape 1,299,299,3
Here the batch_size dimension might differ according to your model. As for the input size dimensions, 299x299 is the default input size for InceptionV3 models.